table of contents
- bookworm 4.18.1-1
- bookworm-backports 4.24.0-2~bpo12+1
- testing 4.24.0-2
- unstable 4.24.0-2
AIO(7) | Miscellaneous Information Manual | AIO(7) |
ИМЯ¶
aio - введение в асинхронный ввод-вывод POSIX
ОПИСАНИЕ¶
Интерфейс асинхронного ввода-вывода POSIX (AIO) позволяет приложениям запускать одну или несколько операций ввода-вывода, которые выполняются асинхронно (т. е., в фоновом режиме). Приложение может выбрать каким образом оно будет уведомлено о завершении операции ввода-вывода: с помощью сигнала, созданием новой нити или вообще не получать уведомления.
Интерфейс POSIX AIO состоит из следующих функций:
- aio_read(3)
- Ставит запрос на чтение в очередь. Это асинхронный аналог read(2).
- aio_write(3)
- Ставит запрос на запись в очередь. Это асинхронный аналог write(2).
- aio_fsync(3)
- Ставит запрос синхронизации операций ввода-вывода над файловым дескриптором. Это асинхронный аналог fsync(2) и fdatasync(2).
- aio_error(3)
- Возвращает информацию о состоянии поставленного в очередь запроса ввода-вывода.
- aio_return(3)
- Возвращает информацию о выполненном запросе ввода-вывода.
- aio_suspend(3)
- Приостанавливает вызывающего до тех пор, пока не выполнится один или более указанных запросов ввода-вывода.
- aio_cancel(3)
- Пытается отменить ожидающие выполнения запросы ввода-вывода над заданным файловым дескриптором.
- lio_listio(3)
- Ставит в очередь сразу несколько запросов ввода-вывода за один вызов функции.
В структуре aiocb («блок управления асинхронным вводом-выводом») задаются параметры, которые управляют операцией ввода-вывода. Аргумент данного типа передаётся во все функции, перечисленные ранее. Данная структура имеет следующий вид:
#include <aiocb.h> struct aiocb {
/* Порядок данных полей определяется реализацией */
int aio_fildes; /* файловый дескриптор */
off_t aio_offset; /* файловое смещение */
volatile void *aio_buf; /* расположение буфера */
size_t aio_nbytes; /* длина передачи */
int aio_reqprio; /* приоритет запроса */
struct sigevent aio_sigevent; /* метод уведомления */
int aio_lio_opcode; /* выполняемая операция;
только в lio_listio() */
/* Не показаны различные поля, используемые в реализациях */ }; /* Operation codes for 'aio_lio_opcode': */ enum { LIO_READ, LIO_WRITE, LIO_NOP };
Поля этой структуры имеют следующее назначение:
- aio_fildes
- Файловый дескриптор, над которым будут выполняться операции ввода-вывода.
- aio_offset
- Файловое смещение, начиная с которого будут выполняться операции ввода-вывода.
- aio_buf
- Буфер, используемый для пересылки данных при операции чтения или записи.
- aio_nbytes
- Размер буфера, на который указывает aio_buf.
- aio_reqprio
- В этом поле задаётся значение, которое вычитается из приоритета реального времени вызывающей нити, чтобы определить приоритет выполнения данного запроса ввода-вывода (смотрите pthread_setschedparam(3)). Указываемое значение должно быть в диапазоне от 0 и до значения, возвращаемого sysconf(_SC_AIO_PRIO_DELTA_MAX). Данное поле игнорируется при операциях синхронизации файла.
- aio_sigevent
- В этом поле задаётся структура, которая указывает как вызывающему должно быть сообщено о завершении анонимной операции ввода-вывода. Возможные значения для aio_sigevent.sigev_notify: SIGEV_NONE, SIGEV_SIGNAL и SIGEV_THREAD. Подробности смотрите в sigevent(7).
- aio_lio_opcode
- Задаёт тип операции, которая будет выполнена; используется только в lio_listio(3).
В дополнении к стандартным функциям, перечисленным ранее, в библиотеке GNU C есть следующее расширение программного интерфейса POSIX AIO:
- aio_init(3)
- Позволяет изменить настройки поведения реализации glibc для POSIX AIO.
ОШИБКИ¶
- EINVAL
- Значение поля aio_reqprio структуры aiocb меньше 0 или больше, чем значение ограничения, возвращаемое вызовом sysconf(_SC_AIO_PRIO_DELTA_MAX).
ВЕРСИИ¶
The POSIX AIO interfaces are provided by glibc since glibc 2.1.
СТАНДАРТЫ¶
POSIX.1-2001, POSIX.1-2008.
ПРИМЕЧАНИЯ¶
Желательно обнулять буфер блока управления перед использованием (смотрите memset(3)). Буфер блока управления и буфер, который задаётся в aio_buf, не должны изменяться во время выполнения операции ввода-вывода. Данные буферы должны оставаться рабочими до завершения операции ввода-вывода.
Одновременное выполнение операций чтения или записи через совместно используемую структуру aiocb приводит к непредсказуемым результатам.
Имеющаяся реализация Linux POSIX AIO предоставляется glibc в пользовательском пространстве. Она имеет ряд ограничений, наиболее существенные из которых — затраты на сопровождение нескольких нитей при операциях ввода-вывода и плохое масштабирование. Некогда для реализации асинхронного ввода-вывода велась работа над ядерной реализацией на основе машины состояний (смотрите io_submit(2), io_setup(2), io_cancel(2), io_destroy(2), io_getevents(2)), но эта реализация ещё недостаточно стабильна в тех местах, где POSIX AIO можно было бы полностью реализовать на системных вызовах ядра.
ПРИМЕРЫ¶
Представленная далее программа открывает все файлы, указанные в параметрах командной строки и ставит в очередь запрос на полученные файловые дескрипторы с помощью aio_read(3). Затем программа входит в цикл, в котором периодически следит за всеми выполняемыми операциями ввода-вывода с помощью aio_error(3). Для каждого запроса ввода-вывода настроено получение уведомления посредством сигнала. После завершения всех запросов ввода-вывода, программа возвращает их состояние с помощью aio_return(3).
The SIGQUIT signal (generated by typing control-\) causes the program to request cancelation of each of the outstanding requests using aio_cancel(3).
Вот результат работы программы. В этом примере программа ставит в очередь два запроса для стандартного ввода, и они отрабатываются двумя введёнными строками «abc» и «x».
$ ./a.out /dev/stdin /dev/stdin открыт /dev/stdin в дескрипторе 3 открыт /dev/stdin в дескрипторе 4 aio_error():
запрос 0 (дескриптор 3): выполняется
запрос 1 (дескриптор 4): выполняется abc Получен сигнал завершения ввода-вывода aio_error():
запрос 0 (дескриптор 3): ввод-вывод завершён
запрос 1 (дескриптор 4): выполняется aio_error():
запрос 1 (дескриптор 4): выполняется x Получен сигнал завершения ввода-вывода aio_error():
запрос 1 (дескриптор 4): ввод-вывод завершён Завершены все запросы ввода-вывода aio_return():
запрос 0 (дескриптор 3): 4
запрос 1 (дескриптор 4): 2
Исходный код программы¶
#include <fcntl.h> #include <stdlib.h> #include <unistd.h> #include <stdio.h> #include <errno.h> #include <aio.h> #include <signal.h> #define BUF_SIZE 20 /* размер буферов для операций чтения */ #define errExit(msg) do { perror(msg); exit(EXIT_FAILURE); } while (0) struct ioRequest { /* определяемая приложением структура для
слежения за запросами ввода-вывода */
int reqNum;
int status;
struct aiocb *aiocbp; }; static volatile sig_atomic_t gotSIGQUIT = 0;
/* при получении SIGQUIT мы пытаемся отменить
все невыполненные запросы ввода-вывода */ static void /* обработчик SIGQUIT */ quitHandler(int sig) {
gotSIGQUIT = 1; } #define IO_SIGNAL SIGUSR1 /* сигнал, уведомляющий о завершении
ввода-вывода */ static void /* обработчик завершения ввода-вывода */ aioSigHandler(int sig, siginfo_t *si, void *ucontext) {
if (si->si_code == SI_ASYNCIO) {
write(STDOUT_FILENO, "Получен сигнал завершения ввода-вывода\n", 31);
/* соответствующая структура ioRequest была бы доступна как
struct ioRequest *ioReq = si->si_value.sival_ptr;
а файловый дескриптор был бы доступен через
ioReq->aiocbp->aio_fildes */
} } int main(int argc, char *argv[]) {
struct sigaction sa;
int s;
int numReqs; /* Total number of queued I/O requests */
int openReqs; /* Number of I/O requests still in progress */
if (argc < 2) {
fprintf(stderr, "Использование: %s <имя_файла> <имя_файла>...\n",
argv[0]);
exit(EXIT_FAILURE);
}
numReqs = argc - 1;
/* Allocate our arrays. */
struct ioRequest *ioList = calloc(numReqs, sizeof(*ioList));
if (ioList == NULL)
errExit("calloc");
struct aiocb *aiocbList = calloc(numReqs, sizeof(*aiocbList));
if (aiocbList == NULL)
errExit("calloc");
/* Establish handlers for SIGQUIT and the I/O completion signal. */
sa.sa_flags = SA_RESTART;
sigemptyset(&sa.sa_mask);
sa.sa_handler = quitHandler;
if (sigaction(SIGQUIT, &sa, NULL) == -1)
errExit("sigaction");
sa.sa_flags = SA_RESTART | SA_SIGINFO;
sa.sa_sigaction = aioSigHandler;
if (sigaction(IO_SIGNAL, &sa, NULL) == -1)
errExit("sigaction");
/* Open each file specified on the command line, and queue
a read request on the resulting file descriptor. */
for (size_t j = 0; j < numReqs; j++) {
ioList[j].reqNum = j;
ioList[j].status = EINPROGRESS;
ioList[j].aiocbp = &aiocbList[j];
ioList[j].aiocbp->aio_fildes = open(argv[j + 1], O_RDONLY);
if (ioList[j].aiocbp->aio_fildes == -1)
errExit("open");
printf("opened %s on descriptor %d\n", argv[j + 1],
ioList[j].aiocbp->aio_fildes);
ioList[j].aiocbp->aio_buf = malloc(BUF_SIZE);
if (ioList[j].aiocbp->aio_buf == NULL)
errExit("malloc");
ioList[j].aiocbp->aio_nbytes = BUF_SIZE;
ioList[j].aiocbp->aio_reqprio = 0;
ioList[j].aiocbp->aio_offset = 0;
ioList[j].aiocbp->aio_sigevent.sigev_notify = SIGEV_SIGNAL;
ioList[j].aiocbp->aio_sigevent.sigev_signo = IO_SIGNAL;
ioList[j].aiocbp->aio_sigevent.sigev_value.sival_ptr =
&ioList[j];
s = aio_read(ioList[j].aiocbp);
if (s == -1)
errExit("aio_read");
}
openReqs = numReqs;
/* Loop, monitoring status of I/O requests. */
while (openReqs > 0) {
sleep(3); /* задержка между проверками */
if (gotSIGQUIT) {
/* On receipt of SIGQUIT, attempt to cancel each of the
outstanding I/O requests, and display status returned
from the cancelation requests. */
printf("получен SIGQUIT; отмена запросов ввода-вывода: \n");
for (size_t j = 0; j < numReqs; j++) {
if (ioList[j].status == EINPROGRESS) {
printf(" Request %zu on descriptor %d:", j,
ioList[j].aiocbp->aio_fildes);
s = aio_cancel(ioList[j].aiocbp->aio_fildes,
ioList[j].aiocbp);
if (s == AIO_CANCELED)
printf("I/O canceled\n");
else if (s == AIO_NOTCANCELED)
printf("I/O not canceled\n");
else if (s == AIO_ALLDONE)
printf("I/O all done\n");
else
perror("aio_cancel");
}
}
gotSIGQUIT = 0;
}
/* Check the status of each I/O request that is still
in progress. */
printf("aio_error():\n");
for (size_t j = 0; j < numReqs; j++) {
if (ioList[j].status == EINPROGRESS) {
printf(" for request %zu (descriptor %d): ",
j, ioList[j].aiocbp->aio_fildes);
ioList[j].status = aio_error(ioList[j].aiocbp);
switch (ioList[j].status) {
case 0:
printf("I/O succeeded\n");
break;
case EINPROGRESS:
printf("In progress\n");
break;
case ECANCELED:
printf("Canceled\n");
break;
default:
perror("aio_error");
break;
}
if (ioList[j].status != EINPROGRESS)
openReqs--;
}
}
}
printf("Завершены все запросы ввода-вывода\n");
/* Check status return of all I/O requests. */
printf("aio_return():\n");
for (size_t j = 0; j < numReqs; j++) {
ssize_t s;
s = aio_return(ioList[j].aiocbp);
printf(" for request %zu (descriptor %d): %zd\n",
j, ioList[j].aiocbp->aio_fildes, s);
}
exit(EXIT_SUCCESS); }
СМОТРИТЕ ТАКЖЕ¶
io_cancel(2), io_destroy(2), io_getevents(2), io_setup(2), io_submit(2), aio_cancel(3), aio_error(3), aio_init(3), aio_read(3), aio_return(3), aio_write(3), lio_listio(3)
«Asynchronous I/O Support in Linux 2.5», Bhattacharya, Pratt, Pulavarty, and Morgan, Proceedings of the Linux Symposium, 2003, https://www.kernel.org/doc/ols/2003/ols2003-pages-351-366.pdf
ПЕРЕВОД¶
Русский перевод этой страницы руководства разработал(и) Dmitry Bolkhovskikh <d20052005@yandex.ru> и Yuri Kozlov <yuray@komyakino.ru>
Этот перевод является свободной программной документацией; он распространяется на условиях общедоступной лицензии GNU (GNU General Public License - GPL, https://www.gnu.org/licenses/gpl-3.0.html версии 3 или более поздней) в отношении авторского права, но БЕЗ КАКИХ-ЛИБО ГАРАНТИЙ.
Если вы обнаружите какие-либо ошибки в переводе этой страницы руководства, пожалуйста, сообщите об этом разработчику(ам) по его(их) адресу(ам) электронной почты или по адресу списка рассылки русских переводчиков.
5 февраля 2023 г. | Справочные страницы Linux 6.03 |