• IO模型复习


    前言

    这一块看的冰河老师的文章,做个复习。

    一. IO的基础概念

    首先来说下什么是IO:涉及计算机核心与其他设备间数据迁移的过程,就是IO。 例如磁盘IO:

    • 输入:从磁盘中读取数据到内存。
    • 输出:将内存中的数据写入磁盘。

    而操作系统发起一次IO操作一般会包含两个阶段:

    1. IO调用:应用程序进程向操作系统内核发起调用。
    2. IO执行操作系统内核完成IO操作。

    其中,IO执行阶段又分为两个阶段:

    1. 准备数据阶段:内核等待I/O设备准备好数据。
    2. 拷贝数据阶段:将数据从内核缓冲区拷贝到用户进程缓冲区。

    如图:
    在这里插入图片描述

    二. IO模型

    IO模型主要有五种类型:

    2.1 阻塞式IO(BIO)

    应用程序进程发起IO调用,但是内核数据还没准备好。因此应用进程一直阻塞等待直到内核数据准备好
    在这里插入图片描述
    缺点:如果内核数据一直没准备好,那用户进程将一直阻塞,浪费性能。


    2.2 非阻塞式IO(NIO)

    鉴于阻塞式IO的缺点,在其基础上,倘若内核数据还没准备好,非阻塞式IO会先将错误信息返回给用户进程,让其无需等待,再通过轮询的方式来请求。在这里插入图片描述优点:相对于阻塞式IO,用户可以无需等待,不会因为内核数据没准备好而进入阻塞状态。
    缺点:频繁的轮询,导致频繁的进行系统调用,消耗大量的CPU资源。


    2.3 IO多路复用(BIO)

    既然频繁的轮询导致CPU消耗很大。那就让内核数据准备好的时候,主动通知应用程序进行系统调用即可。也就是IO多路复用。

    概念:文件描述符(File Descriptor

    文件描述符是非负整数。打开现存文件或新建文件时,内核会返回一个文件描述符。读写文件也需要使用文件描述符来指定待读写的文件。

    IO复用模型核心思路:系统给我们提供一类函数,它们可以同时监控多个fd的操作,任何一个返回内核数据就绪,应用进程再发起系统调用。 注意,这里依旧是需要应用进程去发起系统调用的。

    IO多路复用的方式有三种:select、poll、epoll

    2.3.1 select

    应用进程可以通过select函数,来同时监控多个fd,在select函数监控fd的过程中,只要任何一个数据状态准备就绪了。select就会返回可读状态,这时候应用进程就会发起请求读取内核数据。 如图:
    在这里插入图片描述
    缺点如下:

    1. 监听的IO最大连接数有上限。
    2. select函数返回后,是通过遍历fd集合,找到就绪的描述符fd。(即遍历所有的流

    2.3.2 poll

    鉴于select方式的缺点,就提出了poll。与前者相比,poll解决了连接数量限制的问题。但是poll还是需要通过遍历文件描述符来获取已经就绪的socket

    2.3.3 epoll

    那么为了解决selectpoll存在的问题,就有了IO多路复用(epoll)模型,采用事件驱动来实现
    在这里插入图片描述
    看起来和select的流程图没啥区别,这里加点文字描述:

    1. epoll首先通过epoll_ctl()函数来注册一个文件描述符。
    2. 一旦基于某个fd就绪时,内核会采用回调机制,迅速激活这个fd
    3. 当进程调用epoll_wait()时便得到通知。通过采用监听事件回调的机制来避免遍历所有的文字描述符。

    虽然IO多路复用这种方式对于非阻塞式IO,不需要进行频繁的调用,而是通过回调的方式来进行。但是当进程调用epoll_wait()时,仍然可能被阻塞

    重要的事情说三遍,多路复用IO它依旧是:同步阻塞的!同步阻塞的!同步阻塞的!

    因此从设计上希望有这么个功能(这里我觉得应该这么理解会更好):

    1. 多路复用IO,虽然可以指定对应的IO流。避免遍历所有的IO。
    2. 虽然是通过回调的方式来获取结果的,但是这个等待结果的这个过程,是需要阻塞去等待的。
    3. 因此设计上希望用户进程可以无需等待,先去做别的事情。等回调结果有了,我再去感应即可。

    随之而来的也就是信号驱动IO模型。


    2.4 信号驱动IO(NIO)

    在多路复用的基础上。向内核发送一个信号。此时应用进程不用阻塞,可以去做其他事情。当内核数据准备好后,再通过SIGIO信号通知应用进程。进程一旦获取到信号,就立即调用获取内核数据。如图:
    在这里插入图片描述
    当然,这里数据状态询问流程是异步的没错,但是数据复制部分,依旧是同步阻塞的,也因此这整个信号驱动IO的流程并不是异步的。


    2.5 异步IO(AIO)

    只需要向内核发送一次请求,就可以完成数据状态询问和数据拷贝的所有操作,并且不用阻塞等待结果。
    在这里插入图片描述


    这里解释下BIO、NIO、AIO

    • 同步阻塞(blocking-IO)简称BIO
    • 同步非阻塞(non-blocking-IO)简称NIO
    • 异步非阻塞(asynchronous-non-blocking-IO)简称AIO

    三. 总结

    3.1 select、poll、epoll三者区别☆

    比较项selectpollepoll
    底层数据结构数组链表红黑树+双链表
    获取就绪的fd方式遍历所有遍历所有事件回调
    事件复杂度O(n)O(n)O(1)
    最大连接数1024(Linux)无限制无限制
    fd数据拷贝方式每次调用select,都需要将fd从用户空间拷贝到内核空间每次调用poll,都需要将fd从用户空间拷贝到内核空间通过内存映射(mmap),不需要进行频繁的拷贝fd,一次即可

    3.2 五种IO模型

    IO模型阻塞状态同步状态
    阻塞式IO阻塞同步
    非阻塞式IO非阻塞同步
    IO多路复用阻塞同步
    信号驱动IO非阻塞同步
    异步IO非阻塞异步

    阻塞,非阻塞的概念区分:可以简单理解为需要做一件事能不能立即得到返回应答,如果不能立即获得返回,需要等待,那就阻塞。 更倾向于是否立即应答。

    同步,异步的概念区分:你总是做完一件再去做另一件,不管是否需要时间等待,这就是同步。否则就是异步。 更倾向于是否可以并行做两件事。

    那回过头再看上面的表格:

    非阻塞式IO方面的解释:

    • 非阻塞:因为用户进程能够立刻获得结果(可能是最终用户想要的数据,也可能是错误信息)。
    • 同步:因为数据复制阶段总是在数据询问阶段完成后执行。

    IO多路复用方面的解释:

    • 阻塞:用户进程需要等待回调结果的返回。这一过程是阻塞的。
    • 同步:因为数据复制阶段总是在数据询问阶段完成后执行。

    信号驱动IO方面的解释:

    • 非阻塞:用户进程在数据询问阶段就能够立即获得返回。
    • 同步:需要等待内核发送信号,表示fd找到了。让用户进程得到fd,再由用户进程发起请求进行数据拷贝。

    异步IO方面的解释:

    • 非阻塞:用户也可以立刻获得结果。
    • 异步:整个数据的等待和拷贝操作都交给操作系统来完成,并不是用户,用户无需阻塞等待。

    最终可以发现,针对这五种IO模型,关于异步和同步的区别无非就是:

    • 同步:数据等待和拷贝过程分为两个阶段,由应用进程发起。需要发起两次。
    • 异步:数据等待和拷贝操作都交给操作系统完成。应用进程发起一次请求即可。
  • 相关阅读:
    0725-面试记录
    Pandas数据分析27——pandas画各类图形以及用法参数详解
    企业微信的「举足轻重」
    16:00面试,16:06就出来了,问的问题有点变态。。。
    【C++】继承 ⑬ ( 虚继承原理 | 虚继承解决继承二义性问题 | 二义性产生的原因分析 )
    【前端CSS】网站都变成灰色了,它是怎么实现的?(含源代码解析)
    vue3使用Element Plus的el-table,高亮当前点击的单元格
    MATLAB数字图像处理 实验三:空域频域图像去噪与锐化
    leetcode 50. Pow(x, n)
    如何解决网页中的pdf文件无法下载?pdf打印显示空白怎么办?
  • 原文地址:https://blog.csdn.net/Zong_0915/article/details/125543097