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/。

