From 4076f2893ac3751bbd8002f8a52e60ab4373cbcf Mon Sep 17 00:00:00 2001 From: Michael Kerrisk Date: Sat, 2 Oct 2010 15:12:48 +0200 Subject: [PATCH] aio.7: New page providing an overview of POSIX asynchronous I/O Signed-off-by: Michael Kerrisk --- man7/aio.7 | 458 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 458 insertions(+) create mode 100644 man7/aio.7 diff --git a/man7/aio.7 b/man7/aio.7 new file mode 100644 index 000000000..09995bc77 --- /dev/null +++ b/man7/aio.7 @@ -0,0 +1,458 @@ +'\" t +.\" Copyright (c) 2010 by Michael Kerrisk +.\" +.\" 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. +.\" +.TH AIO 7 2010-10-02 "Linux" "Linux Programmer's Manual" +.SH NAME +aio \- POSIX asynchronous I/O overview +.SH DESCRIPTION +The POSIX asynchronous I/O (AIO) interface allows applications +to initiate one or more I/O operations that are performed +asynchronously (i.e., in the background). +The application can elect to be notified of completion of +the I/O operation in a variety of ways: +by delivery of a signal, by instantiation of a thread, +or no notification at all. + +The POSIX AIO interface consists of the following functions: +.TP 16 +.BR aio_read (3) +Enqueue a read request. +This is the asynchronous analog of +.BR read (2). +.TP +.BR aio_write (3) +Enqueue a write request. +This is the asynchronous analog of +.BR write (2). +.TP +.BR aio_fsync (3) +Enqueue a sync request for the I/O operations on a file descriptor. +This is the asynchronous analog of +.BR fsync (2) +and +.BR fdatasync (2). +.TP +.BR aio_error (3) +Obtain the error status of an enqueued I/O request. +.TP +.BR aio_return (3) +Obtain the return status of a completed I/O request. +.TP +.BR aio_suspend (3) +Suspend the caller until one or more of a specified set of +I/O requests completes. +.TP +.BR aio_cancel (3) +Attempt to cancel outstanding I/O requests on a specified +file descriptor. +.TP +.BR lio_listio (3) +Enqueue multiple I/O requests using a single function call. +.PP +The +.I aiocb +("asynchronous I/O control block") structure defines +parameters that control an I/O operation. +An argument of this type is employed with all of the functions listed above. +This structure has the following form: +.PP +.in +4n +.nf +#include + +struct aiocb { + /* The order of these fields is implementation-dependent */ + + int aio_fildes; /* File descriptor */ + off_t aio_offset; /* File offset */ + volatile void *aio_buf; /* Location of buffer */ + size_t aio_nbytes; /* Length of transfer */ + int aio_reqprio; /* Request priority */ + struct sigevent aio_sigevent; /* Notification method */ + int aio_lio_opcode; /* Operation to be performed; + lio_listio() only */ + + /* Various implementation-internal fields not shown */ +}; + +/* Operation codes for 'aio_lio_opcode': */ + +enum { LIO_READ, LIO_WRITE, LIO_NOP }; + +.fi +.in +The fields of this structure are as follows: +.TP 16 +.I aio_filedes +The file descriptor on which the I/O operation is to be performed. +.TP +.I aio_offset +This is the file offset at which the I/O operation is to be performed. +.TP +.I aio_buf +This is the buffer used to transfer data for a read or write operation. +.TP +.I aio_nbytes +This is the size of the buffer pointed to by +.IR aio_buf . +.TP +.I aio_reqprio +This field specifies a value that is subtracted +from the calling thread's real-time priority in order to +determine the priority for execution of this I/O request. +.BR pthread_setschedparam (3)). +The specified value must be between 0 and the value returned by +.IR sysconf(_SC_AIO_PRIO_DELTA_MAX) . +This field is ignored file synchronization operations. +.TP +.I aio_sigevent +This field is a structure that specifies how the caller is +to be notified when the asynchronous I/O operation completes. +Possible values for +.IR aio_sigevent.sigev_notify +are +.BR SIGEV_NONE , +.BR SIGEV_SIGNAL , +and +.BR SIGEV_THREAD . +See +.BR sigevent (7) +for further details. +.TP +.I aio_lio_opcode +The type of operation to be performed; used only for +.BR lio_listio (3). +.PP +In addition to the standard functions listed above, +the GNU C library provides the following extension to the POSIX AIO API: +.TP 16 +.BR aio_init (3) +Set parameters for tuning the behavior of the glibc POSIX AIO implementation. +.SH NOTES +It is a good idea to zero out the control block buffer before use (see +.BR memset (3)). +The control block buffer and the buffer pointed to by +.I aio_buf +must not be changed while the I/O operation is in progress. +These buffers must remain valid until the I/O operation completes. + +Simultaneous asynchronous read or write operations using the same +.I aiocb +structure yield undefined results. + +The current Linux POSIX AIO implementation is provided in userspace by glibc. +This has a number of limitations, most notably that maintaining multiple +threads to perform I/O operations is expensive and scales poorly. +Work has been in progress for some time on a kernel +state-machine-based implementation of asynchronous I/O +(see +.BR io_submit (2), +.BR io_setup (2), +.BR io_cancel (2), +.BR io_destroy (2), +.BR io_getevents (2)), +but this implementation hasn't yet matured to the point where +the POSIX AIO implementation can be completely +reimplemented using the kernel system calls. +.\" http://lse.sourceforge.net/io/aio.html +.\" http://lse.sourceforge.net/io/aionotes.txt +.\" http://lwn.net/Articles/148755/ +.SH ERRORS +.TP +.B EINVAL +The +.I aio_reqprio +field of the +.I aiocb +structure was less than 0, or greater than the limit specified by +.IR sysconf(_SC_AIO_PRIO_DELTA_MAX) . +.SH VERSIONS +The POSIX AIO interfaces are provided by glibc since version 2.1. +.SH CONFORMING TO +POSIX.1-2001, POSIX.1-2008 +.SH EXAMPLE +The program below opens each of the files named in its command-line +arguments and queues a request on the resulting file descriptor using +.BR aio_read (3). +The program then loops, +periodically monitoring each of the I/O operations +that is still in progress using +.BR aio_error (3). +Each of the I/O requests is set up to provide notification by delivery +of a signal. +After all I/O requests have completed, +the program retrieves their status using +.BR aio_return (3). + +The +.B SIGQUIT +signal (generated by typing control-\\) causes the program to request +cancellation of each of the outstanding requests using +.BR aio_cancel (3). + +Here is an example of what we might see when running this program. +In this example, the program queues two requests to standard input, +and these are satisfied by two lines of input containing +"abc" and "x". + +.in +4n +.nf +$ \fB./a.out /dev/stdin /dev/stdin\fP +opened /dev/stdin on descriptor 3 +opened /dev/stdin on descriptor 4 +aio_error(): + for request 0 (descriptor 3): In progress + for request 1 (descriptor 4): In progress +\fBabc\fP +I/O completion signal received +aio_error(): + for request 0 (descriptor 3): I/O succeeded + for request 1 (descriptor 4): In progress +aio_error(): + for request 1 (descriptor 4): In progress +\fBx\fP +I/O completion signal received +aio_error(): + for request 1 (descriptor 4): I/O succeeded +All I/O requests completed +aio_return(): + for request 0 (descriptor 3): 4 + for request 1 (descriptor 4): 2 +.fi +.in +.SS Program source +\& +.nf +#include +#include +#include +#include +#include +#include + +#define BUF_SIZE 20 /* Size of buffers for read operations */ + +#define errExit(msg) do { perror(msg); exit(EXIT_FAILURE); } while (0) + +#define errMsg(msg) do { perror(msg); } while (0) + +struct ioRequest { /* Application\-defined structure for tracking + I/O requests */ + int reqNum; + int status; + struct aiocb *aiocbp; +}; + +static volatile sig_atomic_t gotSIGQUIT = 0; + /* On delivery of SIGQUIT, we attempt to + cancel all outstanding I/O requests */ + +static void /* Handler for SIGQUIT */ +quitHandler(int sig) +{ + gotSIGQUIT = 1; +} + +#define IO_SIGNAL SIGUSR1 /* Signal used to notify I/O completion */ + +static void /* Handler for I/O completion signal */ +aioSigHandler(int sig, siginfo_t *si, void *ucontext) +{ + write(STDOUT_FILENO, "I/O completion signal received\\n", 31); + + /* The corresponding ioRequest structure would be available as + struct ioRequest *ioReq = si\->si_value.sival_ptr; + and the file descriptor would then be available via + ioReq\->aiocbp\->aio_fildes */ +} + +int +main(int argc, char *argv[]) +{ + struct ioRequest *ioList; + struct aiocb *aiocbList; + struct sigaction sa; + int s, j; + int numReqs; /* Total number of queued I/O requests */ + int openReqs; /* Number of I/O requests still in progress */ + + if (argc < 2) { + fprintf(stderr, "Usage: %s ...\\n", + argv[0]); + exit(EXIT_FAILURE); + } + + numReqs = argc \- 1; + + /* Allocate our arrays */ + + ioList = calloc(numReqs, sizeof(struct ioRequest)); + if (ioList == NULL) + errExit("calloc"); + + aiocbList = calloc(numReqs, sizeof(struct aiocb)); + 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 (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); /* Delay between each monitoring step */ + + if (gotSIGQUIT) { + + /* On receipt of SIGQUIT, attempt to cancel each of the + outstanding I/O requests, and display status returned + from the cancellation requests */ + + printf("got SIGQUIT; canceling I/O requests: \\n"); + + for (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\\n"); + else if (s == AIO_NOTCANCELED) + printf("I/O not canceled\\n"); + else if (s == AIO_ALLDONE) + printf("I/O all done\\n"); + else + errMsg("aio_cancel"); + } + } + + gotSIGQUIT = 0; + } + + /* Check the status of each I/O request that is still + in progress */ + + printf("aio_error():\\n"); + for (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\\n"); + break; + case EINPROGRESS: + printf("In progress\\n"); + break; + case ECANCELED: + printf("Canceled\\n"); + break; + default: + errMsg("aio_error"); + break; + } + + if (ioList[j].status != EINPROGRESS) + openReqs\-\-; + } + } + } + + printf("All I/O requests completed\\n"); + + /* Check status return of all I/O requests */ + + printf("aio_return():\\n"); + for (j = 0; j < numReqs; j++) { + ssize_t s; + + s = aio_return(ioList[j].aiocbp); + printf(" for request %d (descriptor %d): %ld\\n", + j, ioList[j].aiocbp\->aio_fildes, (long) s); + } + + exit(EXIT_SUCCESS); +} +.fi +.SH SEE ALSO +.ad l +.BR io_submit (2), +.BR io_setup (2), +.BR io_cancel (2), +.BR io_destroy (2), +.BR io_getevents (2)), +.BR aio_cancel (3), +.BR aio_error (3), +.BR aio_init (3), +.BR aio_read (3), +.BR aio_return (3), +.BR aio_write (3), +.BR lio_listio (3), +http://www.squid-cache.org/~adrian/Reprint-Pulavarty-OLS2003.pdf