.\" -*- coding: UTF-8 -*- .\" Copyright (c) 2010 by Michael Kerrisk .\" .\" %%%LICENSE_START(VERBATIM) .\" Permission is granted to make and distribute verbatim copies of this .\" manual provided the copyright notice and this permission notice are .\" preserved on all copies. .\" .\" Permission is granted to copy and distribute modified versions of this .\" manual under the conditions for verbatim copying, provided that the .\" entire resulting derived work is distributed under the terms of a .\" permission notice identical to this one. .\" .\" Since the Linux kernel and libraries are constantly changing, this .\" manual page may be incorrect or out-of-date. The author(s) assume no .\" responsibility for errors or omissions, or for damages resulting from .\" the use of the information contained herein. The author(s) may not .\" have taken the same level of care in the production of this manual, .\" which is licensed free of charge, as they might when working .\" professionally. .\" .\" Formatted or processed versions of this manual, if unaccompanied by .\" the source, must acknowledge the copyright and authors of this work. .\" %%%LICENSE_END .\" .\"******************************************************************* .\" .\" This file was generated with po4a. Translate the source file. .\" .\"******************************************************************* .TH AIO 7 "13 августа 2020 г." Linux "Руководство программиста Linux" .SH ИМЯ aio \- введение в асинхронный ввод\-вывод POSIX .SH ОПИСАНИЕ Интерфейс асинхронного ввода\-вывода POSIX (AIO) позволяет приложениям запускать одну или несколько операций ввода\-вывода, которые выполняются асинхронно (т. е., в фоновом режиме). Приложение может выбрать каким образом оно будет уведомлено о завершении операции ввода\-вывода: с помощью сигнала, созданием новой нити или вообще не получать уведомления. .PP Интерфейс POSIX AIO состоит из следующих функций: .TP \fBaio_read\fP(3) Ставит запрос на чтение в очередь. Это асинхронный аналог \fBread\fP(2). .TP \fBaio_write\fP(3) Ставит запрос на запись в очередь. Это асинхронный аналог \fBwrite\fP(2). .TP \fBaio_fsync\fP(3) Ставит запрос синхронизации операций ввода\-вывода над файловым дескриптором. Это асинхронный аналог \fBfsync\fP(2) и \fBfdatasync\fP(2). .TP \fBaio_error\fP(3) Возвращает информацию о состоянии поставленного в очередь запроса ввода\-вывода. .TP \fBaio_return\fP(3) Возвращает информацию о выполненном запросе ввода\-вывода. .TP \fBaio_suspend\fP(3) Приостанавливает вызывающего до тех пор, пока не выполнится один или более указанных запросов ввода\-вывода. .TP \fBaio_cancel\fP(3) Пытается отменить ожидающие выполнения запросы ввода\-вывода над заданным файловым дескриптором. .TP \fBlio_listio\fP(3) Ставит в очередь сразу несколько запросов ввода\-вывода за один вызов функции. .PP В структуре \fIaiocb\fP («блок управления асинхронным вводом\-выводом») задаются параметры, которые управляют операцией ввода\-вывода. Аргумент данного типа передаётся во все функции, перечисленные ранее. Данная структура имеет следующий вид: .PP .in +4n .EX #include 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() */ /* Не показаны различные поля, используемые в реализациях */ }; /* Коды операций для \(aqaio_lio_opcode\(aq: */ enum { LIO_READ, LIO_WRITE, LIO_NOP }; .EE .in .PP Поля этой структуры имеют следующее назначение: .TP \fIaio_fildes\fP Файловый дескриптор, над которым будут выполняться операции ввода\-вывода. .TP \fIaio_offset\fP Файловое смещение, начиная с которого будут выполняться операции ввода\-вывода. .TP \fIaio_buf\fP Буфер, используемый для пересылки данных при операции чтения или записи. .TP \fIaio_nbytes\fP Размер буфера, на который указывает \fIaio_buf\fP. .TP \fIaio_reqprio\fP В этом поле задаётся значение, которое вычитается из приоритета реального времени вызывающей нити, чтобы определить приоритет выполнения данного запроса ввода\-вывода (смотрите \fBpthread_setschedparam\fP(3)). Указываемое значение должно быть в диапазоне от 0 и до значения, возвращаемого \fIsysconf(_SC_AIO_PRIO_DELTA_MAX)\fP. Данное поле игнорируется при операциях синхронизации файла. .TP \fIaio_sigevent\fP В этом поле задаётся структура, которая указывает как вызывающему должно быть сообщено о завершении анонимной операции ввода\-вывода. Возможные значения для \fIaio_sigevent.sigev_notify\fP: \fBSIGEV_NONE\fP, \fBSIGEV_SIGNAL\fP и \fBSIGEV_THREAD\fP. Подробности смотрите в \fBsigevent\fP(7). .TP \fIaio_lio_opcode\fP Задаёт тип операции, которая будет выполнена; используется только в \fBlio_listio\fP(3). .PP В дополнении к стандартным функциям, перечисленным ранее, в библиотеке GNU C есть следующее расширение программного интерфейса POSIX AIO: .TP \fBaio_init\fP(3) Позволяет изменить настройки поведения реализации glibc для POSIX AIO. .SH ОШИБКИ .TP \fBEINVAL\fP Значение поля \fIaio_reqprio\fP структуры \fIaiocb\fP меньше 0 или больше, чем значение ограничения, возвращаемое вызовом \fIsysconf(_SC_AIO_PRIO_DELTA_MAX)\fP. .SH ВЕРСИИ Интерфейсы POSIX AIO появились в glibc в версии 2.1. .SH "СООТВЕТСТВИЕ СТАНДАРТАМ" POSIX.1\-2001, POSIX.1\-2008. .SH ЗАМЕЧАНИЯ Желательно обнулять буфер блока управления перед использованием (смотрите \fBmemset\fP(3)). Буфер блока управления и буфер, который задаётся в \fIaio_buf\fP, не должны изменяться во время выполнения операции ввода\-вывода. Данные буферы должны оставаться рабочими до завершения операции ввода\-вывода. .PP Одновременное выполнение операций чтения или записи через совместно используемую структуру \fIaiocb\fP приводит к непредсказуемым результатам. .PP .\" http://lse.sourceforge.net/io/aio.html .\" http://lse.sourceforge.net/io/aionotes.txt .\" http://lwn.net/Articles/148755/ Имеющаяся реализация Linux POSIX AIO предоставляется glibc в пользовательском пространстве. Она имеет ряд ограничений, наиболее существенные из которых — затраты на сопровождение нескольких нитей при операциях ввода\-вывода и плохое масштабирование. Некогда для реализации асинхронного ввода\-вывода велась работа над ядерной реализацией на основе машины состояний (смотрите \fBio_submit\fP(2), \fBio_setup\fP(2), \fBio_cancel\fP(2), \fBio_destroy\fP(2), \fBio_getevents\fP(2)), но эта реализация ещё недостаточно стабильна в тех местах, где POSIX AIO можно было бы полностью реализовать на системных вызовах ядра. .SH ПРИМЕРЫ Представленная далее программа открывает все файлы, указанные в параметрах командной строки и ставит в очередь запрос на полученные файловые дескрипторы с помощью \fBaio_read\fP(3). Затем программа входит в цикл, в котором периодически следит за всеми выполняемыми операциями ввода\-вывода с помощью \fBaio_error\fP(3). Для каждого запроса ввода\-вывода настроено получение уведомления посредством сигнала. После завершения всех запросов ввода\-вывода, программа возвращает их состояние с помощью \fBaio_return\fP(3). .PP Сигнал \fBSIGQUIT\fP (генерируемый нажатием control\-\e) заставляет программу отменить все невыполненные запросы с помощью \fBaio_cancel\fP(3). .PP Вот результат работы программы. В этом примере программа ставит в очередь два запроса для стандартного ввода, и они отрабатываются двумя введёнными строками «abc» и «x». .PP .in +4n .EX $ \fB./a.out /dev/stdin /dev/stdin\fP открыт /dev/stdin в дескрипторе 3 открыт /dev/stdin в дескрипторе 4 aio_error(): запрос 0 (дескриптор 3): выполняется запрос 1 (дескриптор 4): выполняется \fBabc\fP Получен сигнал завершения ввода\-вывода aio_error(): запрос 0 (дескриптор 3): ввод\-вывод завершён запрос 1 (дескриптор 4): выполняется aio_error(): запрос 1 (дескриптор 4): выполняется \fBx\fP Получен сигнал завершения ввода\-вывода aio_error(): запрос 1 (дескриптор 4): ввод\-вывод завершён Завершены все запросы ввода\-вывода aio_return(): запрос 0 (дескриптор 3): 4 запрос 1 (дескриптор 4): 2 .EE .in .SS "Исходный код программы" \& .EX #include #include #include #include #include #include #include #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, "Получен сигнал завершения ввода\-вывода\en", 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 <имя_файла> <имя_файла>...\en", argv[0]); exit(EXIT_FAILURE); } numReqs = argc \- 1; /* выделяем место под массивы */ struct ioRequest *ioList = calloc(numReqs, sizeof(*ioList)); if (ioList == NULL) errExit("calloc"); struct aiocb *aiocbList = calloc(numReqs, sizeof(*aiocbList)); if (aiocbList == NULL) errExit("calloc"); /* указываем обработчики SIGQUIT и сигнала завершения ввода\-вывода */ 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"); /* открываем каждый файл, заданный в командной строке и ставим в очередь запрос на чтение полученного файлового дескриптора */ for (int 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\en", 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; /* цикл, отслеживание состояние запросов ввода\-вывода */ while (openReqs > 0) { sleep(3); /* задержка между проверками */ if (gotSIGQUIT) { /* при получении SIGQUIT пытаемся отменить каждый невыполненный запрос ввода\-вывода и показываем состояние, возвращаемое при отмене запроса */ printf("получен SIGQUIT; отмена запросов ввода\-вывода: \en"); for (int j = 0; j < numReqs; j++) { if (ioList[j].status == EINPROGRESS) { printf(" Request %d 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\en"); else if (s == AIO_NOTCANCELED) printf("I/O not canceled\en"); else if (s == AIO_ALLDONE) printf("I/O all done\en"); else perror("aio_cancel"); } } gotSIGQUIT = 0; } /* проверяем состояние каждого запроса ввода\-вывода, которые ещё не завершились */ printf("aio_error():\en"); for (int j = 0; j < numReqs; j++) { if (ioList[j].status == EINPROGRESS) { printf(" for request %d (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\en"); break; case EINPROGRESS: printf("In progress\en"); break; case ECANCELED: printf("Canceled\en"); break; default: perror("aio_error"); break; } if (ioList[j].status != EINPROGRESS) openReqs\-\-; } } } printf("Завершены все запросы ввода\-вывода\en"); /* проверяем возвращаемое состояние всех запросов ввода\-вывода */ printf("aio_return():\en"); for (int j = 0; j < numReqs; j++) { ssize_t s; s = aio_return(ioList[j].aiocbp); printf(" запрос %d (дескриптор %d): %zd\en", j, ioList[j].aiocbp\->aio_fildes, s); } exit(EXIT_SUCCESS); } .EE .SH "СМ. ТАКЖЕ" .ad l .nh \fBio_cancel\fP(2), \fBio_destroy\fP(2), \fBio_getevents\fP(2), \fBio_setup\fP(2), \fBio_submit\fP(2), \fBaio_cancel\fP(3), \fBaio_error\fP(3), \fBaio_init\fP(3), \fBaio_read\fP(3), \fBaio_return\fP(3), \fBaio_write\fP(3), \fBlio_listio\fP(3) .PP «Asynchronous I/O Support in Linux 2.5», Bhattacharya, Pratt, Pulavarty, and Morgan, Proceedings of the Linux Symposium, 2003, .UR https://www.kernel.org/doc/ols/2003/ols2003\-pages\-351\-366.pdf .UE .SH ЗАМЕЧАНИЯ Эта страница является частью проекта Linux \fIman\-pages\fP версии 5.10. Описание проекта, информацию об ошибках и последнюю версию этой страницы можно найти по адресу \%https://www.kernel.org/doc/man\-pages/. .PP .SH ПЕРЕВОД Русский перевод этой страницы руководства был сделан Dmitry Bolkhovskikh и Yuri Kozlov . .PP Этот перевод является бесплатной документацией; прочитайте .UR https://www.gnu.org/licenses/gpl-3.0.html Стандартную общественную лицензию GNU версии 3 .UE или более позднюю, чтобы узнать об условиях авторского права. Мы не несем НИКАКОЙ ОТВЕТСТВЕННОСТИ. .PP Если вы обнаружите ошибки в переводе этой страницы руководства, пожалуйста, отправьте электронное письмо на .MT man-pages-ru-talks@lists.sourceforge.net .ME .