0x301 4种主要的IO模型
原文参考:
- https://blog.csdn.net/ldw201510803006/article/details/119767467
- https://www.liaoxuefeng.com/wiki/1016959663602400/1017606916795776
前言
[!QUESTION] IO是什么?
- IO是计算机内存与外部设备进行数据拷贝的过程。
- 由于程序和运行时数据是在内存中驻留,由CPU这个超快的计算核心来执行,涉及到数据交换的地方,通常是磁盘、网络等,就需要IO接操作。
IO模型要解决什么问题
数据读取的基本流程:
- 用户发起IO请求
- 用过调用read()间接调用内核操作从网卡读取数据
- 数据从网卡拷贝到内核缓冲区
- 数据从内核缓冲区拷贝到用户缓冲区
这里面设计到三个硬件:CPU,内存,网卡(外部物理设备),这三者的数据操作速度有巨大的差别,用户读取数据时该阻塞等待?非阻塞轮询获取数据?内核回调通知?这些就是IO模型解决的问题。
IO模型的区别
当用户线程发起IO操作后,网络读取操作会发生以下两个过程
- 数据从网卡拷贝到内核缓冲区
- 数据从内核缓冲区拷贝到用户缓冲区
不同IO模型之间的区别就是:他们实现这两个步骤的方式不同
同步阻塞IO模型(BIO)
什么被阻塞了?用户线程被阻塞了
- 用户线程发起IO调用,执行read方法,用户线程阻塞等待,让出CPU
- 内核等待网卡数据到来 ==> 数据从网卡拷贝到内核缓冲区==> 数据从内核缓冲区拷贝到用户缓冲区
- 内核唤醒用户线程,用户线程执行后续操作
简化版流程图:

用户进程被阻塞时系统在做什么
对于磁盘IO,直接从磁盘中读取到内核缓冲区(这个过程可以不需要cpu参与)。而对于网络IO,应用程序需要等待客户端发送数据,如果客户端还没有发送数据,对应的应用程序将会被阻塞,直到客户端发送了数据,该应用程序才会被唤醒,从Socket协议找中读取客户端发送的数据到内核空间,然后把内核空间的数据copy到用户空间,供应用程序使用。
同步非阻塞IO模型(NIO)
用户线程不断的调用read方法去内核缓冲区查询数据,如果没有数据则返回失败,当有数据时,在数据从内核缓冲区拷贝到用户缓冲区的过程中,用户线程阻塞,让出CPU,等待数据拷贝完成后,唤醒用户进程。
简化流程图:

需要注意的是:这里是用户进程去轮询读取数据,和IO多路复用的专用的线程去轮询处理是不同的。
IO多路复用
参考:[[0x400 IO多路复用]]
只是监测数据
异步非阻塞IO模型
用户线程发起 read 调用的同时注册一个回调函数,read 立即返回,等内核将数据准备好后,再调用指定的回调函数完成处理。在这个过程中,用户线程一直没有阻塞。

异步IO模型中的回调函数通常是在内核数据准备就绪后由内核来调用的,而不是在用户线程中直接调用。这允许用户线程在等待IO操作完成的同时继续执行其他任务,提高了应用程序的并发性和效率。回调函数的执行通常不会阻塞用户线程,它们可以在专门的内核线程或者事件处理线程中执行,或者通过某种机制(如信号)在用户空间中异步执行。
异步非阻塞IO和同步非阻塞IO的区别
异步IO通常适用于需要长时间等待的IO操作,而非阻塞IO适用于需要快速响应的场合。
同步与异步:指应用程序在与内核通信时,数据从内核空间到应用空间的拷贝,是由内核主动发起还是由应用程序来触发;内核主动发起则是异步,反之为同步。