gpt4 book ai didi

异步IO:新时代的IO处理利器

转载 作者:qq735679552 更新时间:2022-09-28 22:32:09 26 4
gpt4 key购买 nike

CFSDN坚持开源创造价值,我们致力于搭建一个资源共享平台,让每一个IT人在这里找到属于你的精彩世界.

这篇CFSDN的博客文章异步IO:新时代的IO处理利器由作者收集整理,如果你对这篇文章有兴趣,记得点赞哟.

异步IO:新时代的IO处理利器

无论是非阻塞IO,IO复用,还是信号驱动式IO,都不是真正意义上的IO,真正的异步IO是数据从内核空间拷贝到用户空间也是异步处理的,拷贝完成,再通知应用进程,应用进程直接读取用户空间的数据进行操作.

到目前为止,我们介绍了阻塞IO,非阻塞IO,信号驱动式IO,IO复用,我们打个形象的比方,来对这几种IO做下区分.

我们去网上买东西,下完单之后,你可以有如下几种处理方式:

  • 下完单之后,在门口一直等待快递小哥把快递送上门,这就是同步阻塞IO;
  • 下完单之后就不管了,直到快递小哥打电话给你通知你去取快递,这就是同步非阻塞IO里面的信号驱动式IO;
  • 下完单之后,你定时的去物流app上面查看你所有快递的状态,只要有快递送到了寄存点,你就去取,这就是同步非阻塞IO里面的IO复用;
  • 下完单之后,你就不管了,直到快递小哥给你送上门,你直接拿到了快递,你不用出门就可以拿到快递了,这就是异步IO。

异步IO最关键的一点就是在读取数据的时候,将IO的buffer提交给内核,让内核往这个buffer写数据.

这节我们就来介绍下异步IO模型和相关API,并且顺便介绍下当下最新的更高性能的IO模型.

阅读完本文,你将了解到:

  • 异步IO的原理;
  • POSIX下定义的异步IO接口以及使用方式;
  • 异步IO的发展方向。

1、异步I/O模型介绍 。

下面是异步IO(asynchronous I/O)的执行流程流程:

异步IO:新时代的IO处理利器

通过异步处理函数如aio_read告知内核启动某个动作,并且让内核在整个操作完成之后再通知应用进程,内核会在把数据复制到用户空间缓冲区之后再进行通知。整个IO过程应用进程都不会被阻塞.

异步IO最大的优化点在于:系统调用是昂贵的,异步IO将轮训等待数据的系统调用(如select,poll,epoll)和读取数据操作合并起来.

下面我们就通过具体了例子来演示下异步IO程序的处理流程.

2、异步IO相关函数使用案例 。

本节我们介绍下POSIX定义的异步操作接口.

2.1、异步IO相关API 。

每个异步函数都需要传入一个aiocb结构(异步IO控制块),这个结构格式如下:

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

};  。

该结构体指定了要异步操作的套接字描述符,操作过程中用到的缓冲,其中aio_sigevent告诉AIO在IO操作完成时,应该指向什么操作.

常见的异步IO相关函数如下:

INT AIO_READ(STRUCT AIOCB *AIOCBP) 。

请求异步读操作,该函数将aiocbp指向的缓冲区描述的I/O请求排队.

注意:aio_read的aiocbp中一定要设置偏移量 。

在传统的非异步read操作中,偏移量是在文件描述符上下文进行维护的,对于每个操作,偏移量都需要更新,以便后续的操作可以对下一块数据进行寻址.

而对于异步read操作来说,可以同时执行很多异步IO read操作,所以这里需要的指明处理的文件的偏移量aiocbp->aio_offset和异步读取的内容的长度aiocbp->aio_nbytes.

aio_read调用后,文件偏移量变为未设置.

INT AIO_WRITE(STRUCT AIOCB *AIOCBP) 。

请求异步写操作,该函数将aiocbp指向的缓冲区描述的I/O请求排队.

aio_write不一定要设置偏移量 。

如果打开的文件,设置了O_APPEND选项,那么偏移量就会被忽略,数据会被附加到文件的末尾;如果未设置O_APPEND,那么从aiocbp->aio_offset开始写入数据,而不考虑文件的偏移量.

SSIZE_T AIO_RETURN(STRUCT AIOCB *AIOCBP) 。

获取完成的异步请求的返回状态.

由于IO异步化了,需要有专门的函数来获取异步处理的状态.

aio_return的返回值即相当于read或write等系统调用的返回值。如果出错,则返回-1,并正确设置errno.

可能的响应值:

  • 成功后,将返回处理的字节数;
  • -1:发生错误,并且设置errno以指示错误原因;

只有在aio_error调用返回EINPROGRESS之外的值之后,才可以调用这个函数,并且只允许调用一次.

INT AIO_ERROR(CONST STRUCT AIOCB *AIOCBP) 。

检查异步请求的状态,可能的响应值:

  • EINPROGRESS:如果请求还没有完成;
  • ECANCELED:如果请求已经被取消;
  • 0:如果请求已完成;
  • 如果异步IO操作失败,则为一个正数的error number,与同步的read(2), write(2), fsync(2),或者 or fdatasync(2)系统的errorno一致。

AIO_SUSPEND 。

int aio_suspend(const struct aiocb * const aiocb_list[],  。

                       int nitems, const struct timespec *timeout);  。

挂起调用进程,直到一个或者多个异步请求完成或失败.

aiocb_list中存放需要等待的异步请求,如:

struct aioct *cblist[MAX_LIST];  。

...  。

cblist[0] = &aiocb1;  。

  。

ret = aio_read( my_aiocb1 );  。

ret = aio_suspend( cblist, MAX_LIST, NULL );  。

INT AIO_CANCEL(INT FD, STRUCT AIOCB *AIOCBP) 。

取消异步IO请求.

LIO_LISTIO 。

int lio_listio(int mode, struct aiocb *const aiocb_list[],  。

                      int nitems, struct sigevent *sevp);  。

发起一系列的IO操作,启动数组aiocb_list描述的I/O操作列表.

下面通过具体例子展示aio的用法.

2.2、aio_read例子 。

如下是一个使用aio_read的例子:

异步IO:新时代的IO处理利器

我把重要的处理步骤都标注起来了,并在代码中做了说明,这里不重复描述,需要注意几点:

  • aio_read的aiocbp中一定要设置偏移量;
  • 一定要在调用aio_error,并且返回值不是EINPROGRESS之后,才调用aio_return后去异步IO处理状态。

以上就是目前异步IO API的设计和基本使用方法.

3、操作系统对异步IO的支持情况 。

3.1、Linux下的异步IO 。

上一节介绍了POSIX下定义的异步操作接口,但是可惜Linux的aio操作不是真正的操作系统级别的支持,而是在用空间中借由GNU库函数由pthread方式实现的,没有对套接字IO进行支持.

基于以上原因,Linux下面,大部分还是通过使用epoll多路复用技术,以及非阻塞IO,通过事件分发模型来构建高性能网络程序.

3.2、Windows下的异步IO 。

Windows实现了一套称为IOCP(I/O Completion Ports,IO完成端口)[1]的完整的异步编程接口。IOCP提供了一种有效的线程模型,用于在多处理器系统上处理多个异步I / O请求.

当进程创建IOCP时,系统会为请求创建关联的队列对象,其唯一目的是为这些请求提供服务.

一个进程通过将IOCP与预分配的线程池结合使用,来处理许多并发异步IO请求,相比于通过在接收IO请求时创建线程,会更快,更高效.

基于IOCP,产生了Proactor模式,一种与Reactor模式类似,但是更加高效的模式.

这里是不是看的有点不太懂,没关系,在后续高性能网络编程范式章节中,我们会详细介绍这两种模式.

4、更高效的IO 。

4.1、背景 。

由于Linux下并没有广泛被采用的AIO技术,aio系列的函数是有POSIX定义的异步操作接口,并不是真正操作系统内核支持的异步IO.

目前最流行的还是基于epoll的多路复用技术,以及依托多路复用技术产生的Reactor模式.

为了推动AIO在Linux系统的发展,实现更加高效的IO,于是后来变有了io_uring.

4.2、io_uring 。

io_uring是在Linux Kernel 5.1中添加的,用于替代AIO和io_submit,构造通用的异步系统调用接口.

关于异步IO就介绍到这里,在下一篇文章中,我们会详细探讨使用各种IO模型的高性能网络编程范式.

博客链接:https://www.itzhai.com 。

最后此篇关于异步IO:新时代的IO处理利器的文章就讲到这里了,如果你想了解更多关于异步IO:新时代的IO处理利器的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com