0%

异步非阻塞I/O


专注于用户空间的系统级编程–即内核之上的所有内容。

对I/O操作的分类主要基于时间和占有两个角度进行划分:
从时间角度可分为同步操作和异步操作;
从资源占有角度可分为阻塞操作和非阻塞操作。
从而UNXI可以构造出4种不同的I/O操作模型:

  • 同步阻塞I/O
  • 同步非阻塞I/O
  • 异步阻塞I/O
  • 异步非阻塞I/O,AIO

AIO是将I/O操作与应用程序的计算操作重叠执行的一种模型,即实现I/O操作与计算操作的有效分离。
AIO背后的基本思想是允许进程发起很多I/O操作,而不用阻塞或等待任何操作完成。
异步非阻塞I/O模型是一种处理与I/O重叠进行的模型。
读请求会立即返回,说明read请求已经成功发起了。
在后台完成读操作时,应用程序然后会执行其他处理操作。
当read的响应到达时,就会产生一个信号或执行一个基于线程的回调函数来完成这次 I/O 处理过程。

在一个进程中为了执行多个I/O请求而对计算操作和I/O处理进行重叠处理的能力利用了处理速度与 I/O 速度之间的差异。
当一个或多个I/O请求挂起时,CPU可以执行其他任务;
或者更为常见的是,在发起其他I/O的同时对已经完成的I/O进行操作。

AIO在当前Linux下主要有两种AIO实现技术:一种是glibc库函数实现,另一种是Linux内核实现,并由libaio库进行封装

glibc库中的AIO实现,需要在编译时-lrt

#include <aio.h>

//请求异步读操作
int aio_read(struct aiocb *aiocbp);
//检查异步请求的状态
int aio_error(const struct aiocb *aiocbp);
//获得完成的异步请求的返回状态
ssize_t aio_return(struct aiocb *aiocbp);
//请求异步写操作
int aio_write(struct aiocb *aiocbp);
//挂起调用进程,直到一个或多个异步请求已经完成(或失败)
int aio_suspend(const struct aiocb * const aiocb_list[],
                int nitems, const struct timespec *timeout);
//取消异步 I/O 请求
int aio_cancel(int fd, struct aiocb *aiocbp);
//发起一系列 I/O 操作
int lio_listio(int mode, struct aiocb *const aiocb_list[],
                int nitems, struct sigevent *sevp);

在异步非阻塞I/O中,我们可以同时发起多个传输操作。
这需要每个传输操作都有惟一的上下文,
这样我们才能在它们完成时区分到底是哪个传输操作完成了。
在 AIO 中,这是一个 aiocb(AIO I/O Control Block)结构。
这个结构包含了有关传输的所有信息,包括为数据准备的用户缓冲区。
在产生 I/O (称为完成)通知时,aiocb 结构就被用来惟一标识所完成的 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 */
};

sigevent 结构告诉 AIO 在 I/O 操作完成时应该执行什么操作。
我们将在 AIO 的展示中对这个结构进行探索。
现在我们将展示各个 AIO 的 API 函数是如何工作的,以及我们应该如何使用它们。

AIO通知:通过信号实现异步通知和通过函数回调来实现

  1. 网络编程–IO模型示例
  2. 使用异步 I/O 大大提高应用程序的性能
  3. Linux kernel AIO这个奇葩
  4. Linux下的服务器开发模型 Epoll和AIO的讨论
  5. Epoll vs. IOCP
  6. 异步,epoll和aio

转载:

异步IO就是把IO提交给系统,让系统替你做,做完了再用某种方式通知你(通过信号,或者其他异步方式
通知,这时候,操作系统已经帮你完成IO操作,具体来说就是你那个作为传入参数的的buffer的指针所指向
的空间已经读到了数据或者你的buffer的数据已经写出去了);

非阻塞IO就是你要通过某种方式不定时地向系统询问你是否可以开始做某个IO( 轮询啊 ),当可以开始
后,是要自己来完成IO(也就是说还要自己调用一次read来填充buffer或者write来不buffer的数据写出去);

epoll和aio(这里的aio是指linux 2.6内核后提供的aio api)区别:

aio是异步非阻塞的。其实是aio是用线程池实现了异步IO。

epoll在这方面的定义上有点复杂,首先epoll的fd集里面每一个fd都是非阻塞的,但是epoll(还
select poll )在调用时阻塞等待fd可用,然后epoll只是一个异步通知机制,只是在fd可用时通知你,并
没有做任何IO操作,所以不是传统的异步。