5种IO模型学习笔记 | 青训营

2023年 8月 26日 43.5k 0

5种IO模型学习笔记 | 青训营

要了解5种IO模型,首先要明白IO操作会导致操作系统在用户态和内核态之间来回切换。

​ 为了避免用户应用导致冲突甚至内核崩溃,用户应用与内核是分离的:

  • 进程的寻址空间会划分为两部分:内核空间、用户空间
  • 用户空间只能执行受限的命令,而且不能直接调用系统资源,必须通过内核提供的接口来访问
  • 内核空间可以执行特权命令,调用一切系统资源

Linux系统为了提高IO效率,会在用户空间和内核空间都加入缓冲区:

  • 写数据时,要把用户缓冲数据拷贝到内核缓冲区,然后写入设备
  • 读数据时,要从设备读取数据到内核缓冲区,然后拷贝到用户缓冲区

1692968952390.png

一、阻塞式IO( Blocking IO)

阻塞IO在两个阶段都会等待数据。

阶段一:

  • 用户进程尝试读取数据(比如网卡数据)
  • 此时数据尚未到达,内核需要等待数据
  • 此时用户进程也处于阻塞状态
  • 阶段二:

  • 数据到达并拷贝到内核缓冲区,代表已就绪
  • 将内核数据拷贝到用户缓冲区
  • 拷贝过程中,用户进程依然阻塞等待
  • 拷贝完成,用户进程解除阻塞,处理数据
  • 二、非阻塞式IO(New/No Blocking IO)

    非阻塞IO操作会立即返回结果而不是阻塞用户进程。

    阶段一:

  • 用户进程尝试读取数据(比如网卡数据)
  • 此时数据尚未到达,内核需要等待数据
  • 返回异常给用户进程
  • 用户进程拿到error后,再次尝试读取
  • 循环往复,直到数据就绪
  • 阶段二:

  • 将内核数据拷贝到用户缓冲区
  • 拷贝过程中,用户进程依然阻塞等待
  • 拷贝完成,用户进程解除阻塞,处理数据
  • 非阻塞IO模型中,用户进程在第一个阶段是非阻塞,第二个阶段是阻塞状态。虽然是非阻塞,但性能并没有得到提高。而且忙等机制会导致CPU空转,CPU使用率暴增。

    三、IO多路复用

    无论是阻塞IO还是非阻塞IO,用户应用在一阶段都需要调用recvfrom方法来获取数据,差别在于无数据时的处理方案:

    • 如果调用recvfrom时,恰好没有数据,阻塞IO会使CPU阻塞,非阻塞IO使CPU空转,都不能充分发挥CPU的作用。
    • 如果调用recvfrom时,恰好有数据,则用户进程可以直接进入第二阶段,读取并处理数据

    而在单线程情况下,只能依次处理IO事件,如果正在处理的IO事件恰好未就绪(数据不可读或不可写),线程就会被阻塞,所有IO事件都必须等待,性能自然会很差。

    IO多路复用是利用单个线程来同时监听多个IO事件,并在某个事件可读、可写时得到通知,从而避免无效的等待,充分利用CPU资源。不过监听IO事件的方式、通知的方式又有多种实现,常见的有:

    • select
    • poll
    • epoll

    四、信号驱动

    • 信号驱动IO是与内核建立SIGIO的信号关联并设置回调,当内核有IO事件就绪时,会发出SIGIO信号通知用户,期间用户应用可以执行其它业务,无需阻塞等待。
    • 当有大量IO操作时,信号较多,SIGIO处理函数不能及时处理可能导致信号队列溢出,而且内核空间与用户空间的频繁信号交互性能也较低。

    五、异步IO(Asynchronous IO)

    异步IO的整个过程(两个阶段)都是非阻塞的,用户进程调用完异步API后就可以去做其它事情,内核等待数据就绪并拷贝到用户空间后才会递交信号,通知用户进程。

    六、关于5种模型的对比

    1、IO操作是同步还是异步,关键看数据在内核空间与用户空间的拷贝过程(数据读写的IO操作),也就是阶段二是同步还是异步:

    image-20230825212048219.png

    2、一个简单的例子比较5种IO模型

    假设你现在是个大厨(炖个老母鸡汤,切点土豆丝/姜丝/葱丝):

    • 同步/阻塞:你站在锅边,一直等到汤炖好,期间不能做其他事情,直到汤炖好才去处理其他任务。
    • 同步/非阻塞:你不断地查看锅里的汤,看是否炖好。在检查的间隙,你可以处理其他任务,如切菜。但你需要不断地切换任务,确保汤炖好了就可以处理。
    • 异步/信号驱动:你给锅安装一个传感器,当汤炖好时,传感器会发出信号提醒你。在此期间,你可以处理其他任务,而不用担心错过汤炖好的时机。
    • 异步 I/O:你请了一个助手,让他负责炖汤。当汤炖好时,助手会通知你。你可以专心处理其他任务,而无需关心炖汤的过程。

    七、笔记总结

    简单总结一下,IO 模型主要有五种:阻塞 I/O、非阻塞 I/O、多路复用、信号驱动和异步 I/O。

    • 阻塞 I/O:应用程序执行 I/O 操作时,会一直等待数据传输完成,期间无法执行其他任务。
    • 非阻塞 I/O:应用程序执行 I/O 操作时,如果数据未准备好,立即返回错误状态,不等待数据传输完成,可执行其他任务。
    • 多路复用:允许一个线程同时管理多个 I/O 连接,适用于高并发、低延迟和高吞吐量场景,减少线程数量和上下文切换开销。
    • 信号驱动:依赖信号通知应用程序 I/O 事件,适用于低并发、低延迟和低吞吐量场景,需要为每个 I/O 事件创建信号和信号处理函数。
    • 异步 I/O:应用程序发起 I/O 操作后,内核负责数据传输过程,完成后通知应用程序。应用程序无需等待数据传输,可执行其他任务。

    Redis在处理操作命令的线程中就使用的是IO多路复用模型,也就是使用单个线程去处理多个Socket连接,当操作命令就绪后,将IO事件转发给三个处理器去异步执行。Redis的单线程能够处理很高并发量请求与IO多路复用模型有着密切关系。

    相关文章

    服务器端口转发,带你了解服务器端口转发
    服务器开放端口,服务器开放端口的步骤
    产品推荐:7月受欢迎AI容器镜像来了,有Qwen系列大模型镜像
    如何使用 WinGet 下载 Microsoft Store 应用
    百度搜索:蓝易云 – 熟悉ubuntu apt-get命令详解
    百度搜索:蓝易云 – 域名解析成功但ping不通解决方案

    发布评论