AIO - Linux手册页
Linux程序员手册 第7部分
更新日期: 2020-08-13
名称
aio-POSIX异步I / O概述
说明
POSIX异步I / O(AIO)接口允许应用程序启动一个或多个异步执行的I / O操作(即在后台)。可以通过多种方式选择通过I / O操作完成来通知应用程序:通过传递信号,通过线程实例化,或者完全不通知。
POSIX AIO接口包含以下功能:
- aio_read(3)
- 排队读取请求。这是read(2)的异步模拟。
- aio_write(3)
- 排队写请求。这是write(2)的异步模拟。
- aio_fsync(3)
- 使对文件描述符的I / O操作的同步请求入队。这是fsync(2)和fdatasync(2)的异步模拟。
- aio_error(3)
- 获取排队的I / O请求的错误状态。
- aio_return(3)
- 获取已完成的I / O请求的返回状态。
- aio_suspend(3)
- 挂起呼叫者,直到完成一组指定的I / O请求中的一个或多个。
- aio_cancel(3)
- 尝试取消对指定文件描述符的未完成I / O请求。
- lio_listio(3)
- 使用单个函数调用排队多个I / O请求。
aiocb("异步I / O控制块")结构定义控制I / O操作的参数。上面列出的所有函数都使用此类型的参数。该结构具有以下形式:
#include <aiocb.h> 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 aqaio_lio_opcodeaq: */ enum { LIO_READ, LIO_WRITE, LIO_NOP };
此结构的字段如下:
- aio_fildes
- 要对其执行I / O操作的文件描述符。
- aio_offset
- 这是要执行I / O操作的文件偏移量。
- aio_buf
- 这是用于传输数据以进行读或写操作的缓冲区。
- aio_nbytes
- 这是aio_buf指向的缓冲区的大小。
- aio_reqprio
- 该字段指定从调用线程的实时优先级中减去的值,以确定执行此I / O请求的优先级(请参阅pthread_setschedparam(3))。指定的值必须介于0和sysconf(_SC_AIO_PRIO_DELTA_MAX)返回的值之间。对于文件同步操作,将忽略此字段。
- aio_sigevent
- 此字段是一种结构,用于指定异步I / O操作完成时如何通知调用方。 aio_sigevent.sigev_notify的可能值为SIGEV_NONE,SIGEV_SIGNAL和SIGEV_THREAD。有关更多详细信息,请参见sigevent(7)。
- aio_lio_opcode
- 要执行的操作类型;仅用于lio_listio(3)。
除了上面列出的标准功能之外,GNU C库还为POSIX AIO API提供了以下扩展:
- aio_init(3)
- 设置参数以调整glibc POSIX AIO实现的行为。
错误说明
- EINVAL
- aiocb结构的aio_reqprio字段小于0,或大于调用sysconf(_SC_AIO_PRIO_DELTA_MAX)返回的限制。
版本
从2.1版开始,glibc提供POSIX AIO接口。
遵循规范
POSIX.1-2001,POSIX.1-2008。
备注
在使用之前将控制块缓冲区清零是一个好主意(请参阅memset(3))。在I / O操作进行期间,不得更改控制块缓冲区和aio_buf指向的缓冲区。这些缓冲区必须保持有效,直到I / O操作完成。
使用相同的aiocb结构进行的同时异步读取或写入操作会产生不确定的结果。
glibc在用户空间中提供了当前的Linux POSIX AIO实现。这有很多限制,最明显的是,维护多个线程来执行I / O操作非常昂贵,并且伸缩性很差。基于内核状态机的异步I / O实现已经进行了一段时间(请参阅io_submit(2),io_setup(2),io_cancel(2),io_destroy(2),io_getevents(2)),但是该实现尚未成熟到可以使用内核系统调用完全重新实现POSIX AIO实现的地步。
示例
下面的程序打开在其命令行参数中命名的每个文件,并使用aio_read(3)在生成的文件描述符上对请求进行排队。然后,程序循环运行,并使用aio_error(3)定期监视仍在进行的每个I / O操作。每个I / O请求都设置为通过传递信号来提供通知。完成所有I / O请求后,程序将使用aio_return(3)检索其状态。
SIGQUIT信号(通过键入control- \生成)使程序使用aio_cancel(3)请求取消每个未完成的请求。
这是运行该程序时可能看到的示例。在此示例中,程序将两个请求排队到标准输入中,并且这些请求由包含" abc"和" x"的两行输入满足。
$ ./a.out /dev/stdin /dev/stdin 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 abc 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 x 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
Program source
#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 /* 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) { if (si->si_code == SI_ASYNCIO) { 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 <pathname> <pathname>...\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): %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)
" Linux 2.5中的异步I / O支持",Bhattacharya,Pratt,Pulavarty和Morgan,Linux研讨会论文集,2003年,
出版信息
这个页面是Linux手册页项目5.08版的一部分。有关项目的说明、有关报告错误的信息以及此页面的最新版本,请访问https://www.kernel.org/doc/man-pages/。