异步 I/O 是针对同步 I/O 提出的概念,它不需要线程等待 I/O 结果,而只需要请求进行传输,然后系统会自动完成 I/O 传输,结束或者出现错误时会产生相应的 I/O 信号,用户程序只需要设置好对应的信号陷入函数,即可处理一个异步 I/O 事件。
#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; /* 异步IO完成后,通知进程的方式,详细见下面 */ int aio_lio_opcode; /* Operation to be performed;lio_listio() only */ }; struct sigevent{ int sigev_notify; // 通知类型 int sigev_signo; // 信号值 union sigval sigev_value; // 回调函数的参数 void (*sigev_notify_function)(union sigval); // 回调函数 pthread_attr_t *sigev_notify_attributes; // }; sigev_notify的取值有以下三种:
SIGEV_NONE:异步I/O请求完成后,不通知进程
SIGEV_SIGNAL:异步I/O请求完成后,产生由sigev_signo字段指定的信号。如果应用程序已选择捕捉信号,且在建立信号处理程序的时候指定了SA_SIGINFO标志,那么该信号将被入队(如果实现支持排队信号),信号处理程序会传送给一个siginfo结构,该结构的si_value字段被设置为sigev_value
SIGEV_THREAD 当异步I/O请求完成后,由sigev_notify_function指定的函数被调用,sigev_value是唯一的参数。除非sigev_notify_attributes字段被设定为pthread属性结构的地址,且结构指定了另外的线程属性,否则该函数将在分离状态下的单独的线程下执行。
#include
int aio_read(struct aiocb *aiocbp);
int aio_write(struct aiocb *aiocbp);
int aio_fsync(int op,struct aiocb *aiocbp);
int aio_error(const struct aiocb *aiocbp);
ssize_t aio_return(const struct aiocb *aiocbp);
int aio_suspend(const struct aiocb *list,int nent,const struct timespec* timeout);
int aio_cancel(int op,struct aiocb *aiocbp);
异步IO的操作流程大概如下:
(1)使用aio_read()或者aio_write()将操作请求放入到等待处理的队列中。这两个函数的返回值与实际操作结果并没有任何关系
(2)aio_fsync()函数用于将aio_write()的写请求同步到硬盘,op指定异步的方式,若op为O_DSYNC则表示只是更新硬盘文件内容,若op为O_SYNC则表示等待实际I/O结束才返回,从而确保修改过的块立即写到硬盘上。
(3)aio_error()是为了获取到一个异步读写或同步的状态,返回值有以下几种情况:
0:异步操作完成,可以调用aio_return()函数获取操作返回值
-1:aio_error函数调用失败
EINPROGRESS:异步读写或同步仍未完成
(4)如果aio_error()获取到异步操作已经完成,则可以使用函数aio_return()获取异步操作的返回值,此时必须确保异步读写或同步已经完成,函数调用失败返回-1.
(5)如果在完成了所有事务之后,异步IO仍然没有完成,可以使用aio_suspend()阻塞进程,直到所有的操作完成,其中list是AIO控制块数组,nent表明了数组的个数。aio_suspend()的返回值有三种:(1)如果被信号中断,它将返回-1,并将errno设置为EINTR.(2)如果timeout超时,它将返回-1,并将errno设置为EAGAIN,timeout为NULL则表示不限时(3)如果返回值为0,则表示所有异步IO都已经完成,将不阻塞直接返回
(6)当我们不想等待异步IO的操作时,可以使用函数aio_cancel()来尝试取消他们。fd为操作的文件描述符,aiocbp为操作的数据块,如果aiocbp为NULL,则表示取消fd上面的所有异步IO操作。返回值有以下4种情况:
AIO_ALLDONE:所有异步IO操作在尝试取消他们之前都已经完成
AIO_CANCELED:所有要求的操作已被取消
AIO_NOTCANCELED:至少一个操作请求没有被取消。
-1:函数调用失败
还有一个函数,它既可以以同步接口调用,也可以以异步接口调用。
#include
int aio_listio(int mode,struct aiocb* list,int nent,struct sigevent* sigev);
mode参数决定该函数是异步还是同步IO,如果该参数被设置为LIO_WAIT,则表示该函数是同步的,即在所有操作完成后,函数才返回,此时sigev将被忽略。如果被设置为LIO_NOWAIT,函数将是异步IO,即只是将访问请求放在队列中即返回,在所有IO结束后,按照sigev参数指定的方式被异步通知,如果不想被通知,可以将sigev设置为NULL。注意,每个控制块都有自己在完成后的通知方式,这里的sigev是另外附加的,它是是所有操作完成后,进行通知。
list是控制块的列表,nent表示个数。