Provided by: manpages-ru_4.18.1-1_all bug

ИМЯ

       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 версии 3 ⟨https://www.gnu.org/licenses/gpl-3.0.html⟩ или более позднюю, чтобы
       узнать об условиях авторского права. Мы не несем НИКАКОЙ ОТВЕТСТВЕННОСТИ.

       Если вы обнаружите ошибки в переводе этой страницы руководства, пожалуйста, отправьте
       электронное письмо на ⟨man-pages-ru-talks@lists.sourceforge.net⟩.