POSIX AIO -- glibc 版本异步 IO 简介

2015-05-24 09:19:04   最后更新: 2015-06-24 13:18:32   访问数量:2005




linux 中最常用的 IO 模型是同步 IO,在这个模型中,请求发出后应用程序会阻塞直到满足条件(阻塞 IO),或在不满足条件的情况下立即返回出错(非阻塞 IO),这样做的好处是程序在等待 IO 请求完成时不会占用 CPU

POSIX 定义了异步 IO 应用程序接口(AIO API),linux 2.6 以上版本的内核也实现了内核级别的异步 IO 调用

异步 IO 的基本思想是允许进程发起很多 IO 操作,而不用阻塞任何一个,也不用等待任何操作的完成,直到 IO 操作完成时,进程可以检索 IO 操作的结果

 

linux 下主要有两套异步 IO,分别是 glibc 实现版本,和 linux 内核实现、libaio 封装的版本

 

write、read 如果没有设置 O_NONBLOCK 标识则为同步阻塞式 IO,一旦 IO 发起,则进程一直等待直到操作完成

设置了 O_NONBLOCK 标识后,write、read 成为非阻塞 IO,调用后如果资源可用则进行操作,并立即返回,如果资源不可用则直接返回出错,这样的情况下,程序通常需要进入忙等待状态,反复调用 IO 操作,直到正常返回

以上两种 IO 模型虽然可以很好地完成单机的 IO 操作,但是对于并发的请求则无法实现

 

对于并发的多个请求,可以使用 IO 复用模型,如 select、poll、epoll 等,但是进程必须阻塞直到操作完成

如果需要进行并发、非阻塞的 IO 操作,比如 CPU 密集型应用及较慢的 IO 操作应用场景下,使用异步 IO 是一个很好地选择

 

同步阻塞式 IO 模型

 

 

同步非阻塞 IO 模型

 

 

异步 IO 模型

 

 

glibc 版本异步 IO 主要包含以下接口(全部定义于 aio.h 中,调用时必须使用 POSIX 实时扩展库 librt):

glibc 版本异步 IO 调用接口
函数功能原型
aio_read请求异步读操作int aio_read(struct aiocb *aiocbp);
aio_write请求异步写操作int aio_write(struct aiocb *aiocbp);
aio_error检查异步请求的状态int aio_error(const struct aiocb *aiocbp);
aio_return获得完成的异步请求的返回状态ssize_t aio_return(struct aiocb *aiocbp);
aio_suspend挂起调用进程,直到一个或多个异步请求已经完成(或失败)int aio_suspend(const struct aiocb * const list[], int nent, const struct timespec *timeout);
aio_cancel取消异步 I/O 请求int aio_cancel(int fildes, struct aiocb *aiocbp);
lio_listio同时发起多个异步IO传输int lio_listio( int mode, struct aiocb *list[], int nent, struct sigevent *sig );

 

aiocb 结构

上述函数用到了一个 struct aiocb 结构

主要包含以下字段:

struct aiocb { int aio_fildes; // 要被读写的文件描述符 volatile void *aio_buf; // 读写操作的内存 buffer __off64_t aio_offset; // 读写操作的文件偏移 size_t aio_nbytes; // 需要读写的字节长度 int aio_reqprio; // 请求优先级 struct sigevent aio_sigevent; // 异步操作完成后的信号或回调函数 /* Internal fields */ ... };

 

 

上述结构中有一个 aio_sigevent 域,用于定义异步操作完成时通知信号或回调函数

struct sigevent { int sigev_notify; // 响应类型 int sigev_signo; // 信号 union sigval sigev_value; // 信号传递的参数 void (*sigev_notify_function)(union sigval); // 回调函数 pthread_attr_t *sigev_notify_attributes; // 线程回调 }

 

 

上述结构中用到了一个联合体 sigval:

typedef union sigval { int sival_int; void *sival_ptr; } sigval_t;

 

通常被称为“信号的 4 字节值”,制定了信号传递的参数

 

aio_read、aio_write

int aio_read( struct aiocb *aiocbp ); int aio_write( struct aiocb *aiocbp );

 

调用成功返回 0,失败返回 -1 并设置 errno

 

将请求添加到 request_queue

通过参数 aiocbp 指向的结构可以设置文件描述符、文件偏移量、缓冲区及大小等属性,函数执行后立即返回

对于 aio_write,如果设置了 O_APPEND,则文件偏移量属性会被忽略

 

aio_error

int aio_error( struct aiocb *aiocbp );

 

 

用于查询请求的状态

返回值如下:

aio_error 函数返回值
返回值意义
EINPROGRESS请求尚未完成
ECANCELLED请求已经被用用程序取消
-1调用出错,出错原因查看 errno

 

aio_return

ssize_t aio_return( struct aiocb *aiocbp );

 

 

获取异步 IO 返回值

调用成功返回读写的字符数,出错返回 -1

 

aio_suspend

int aio_suspend( const struct aiocb *const cblist[], int n, const struct timespec *timeout );

 

 

阻塞进程,直到列表中的某个异步请求完成

cblist 中任何一个异步请求完成,函数都会返回 0,出错返回 -1

 

aio_cancel

int aio_cancel( int fd, struct aiocb *aiocbp );

 

 

取消一个异步请求,第二个参数为 NULL 则取消所有该 fd 上的异步请求

成功取消返回 AIO_CANCELED,请求已经完成则返回 AIO_NOTCANCELED

在取消多个请求的情况下,如果至少有一个请求没有被取消,则返回 AIO_NOT_CANCELED,如果没有一个请求可以被取消,则返回 AIO_ALLDONE

 

lio_listio

int lio_listio( int mode, struct aiocb *list[], int nent, struct sigevent *sig );

 

 

同时发起多个异步请求,可以很大程度上提高系统的性能

 

mode 参数可选 LIO_WAIT 或 LIO_NOWAIT 来声明该函数是否阻塞

nent 参数定义了 list 列表的最大元素个数

list 列表中可以有值为 NULL 的请求,则该请求被忽略

sigevent 的指针定义了在所有 IO 操作都完成时产生的信号或调用的回调函数

 






读书笔记      技术帖      linux      apue      unix环境高级编程      io      read      write      技术分享      线程      signal      信号      高级io      glibc      aio      input      output      aio_read      aio_write      内核      异步io      同步io      阻塞     


京ICP备15018585号