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

概述

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

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

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

 

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

IO 模型简介

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

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

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

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

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

POSIX AIO -- glibc 版本异步 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 操作都完成时产生的信号或调用的回调函数

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值