linux IO 模型介绍

张彤 2021年10月31日 1,141次浏览

近期翻看centos IO模型的时候,外网找到一篇文章,感觉解释清晰,配图精美,现在以原文为主干,辅以自己的一些理解,介绍Linux IO模型。
原文地址:Five IO models of UNIX

五种UNIX IO模型

UNIX IO 模型和 Java IO模型的联系

  • 五种Unix IO 模型
序号IO模型中文翻译
1blocking IO阻塞
2non blocking IO非阻塞
3IO multiplexing多路复用
4signal driven IO信号驱动模式
5asynchronous IO异步
  • UNIX IO与JAVA IO 的关系
Java IO modelUnix IO model
BIOBlocking IO
NIOIO multiplexing
AIOAsynchronous IO

内核态与用户态

kernel_user.png

在聊IO模型之前,先要讲一下数据(进程)的用户态和内核态(user state and kernel state.)
我们将磁盘中的文件加载到内存中,系统会怎么作呢?

  1. 由于我们的所有程序与操作系统的内核交互,因此首先将文件从磁盘加载到内核,然后文件处于内核状态(kernel state)。
  2. 该文件从内核重新加载到内存,然后可以在内存中读取和写入应用程序。此时,该文件处于内核模式(kernel model)。
  3. 因此UNIX的五个IO模型的差异指出,这两个步骤的处理流程是不同的。

五种IO模式分别如下:

Blocking IO

blocking.jpg

  • 这一步非常简单,首先系统调用recvfrom函数后,线程就会一直等待直到:
    第一步是加载文件到内核模式
    第二步是加载文件到用户模式

  • 在linux中,默认情况下所有的socket都是blocking。它符合人们最常见的思考逻辑。阻塞就是进程 "被" 休息, CPU处理其它进程去了。

Non-Blocking IO

Nonblocking.jpg

  • 系统不断推送 recvfrom Poll 直到第一步完成,然后第二步,将数据由内核态转向用户态阻塞。
  • 这里的非阻塞IO模式主要是指第一步,将数据加载到内核态。 这个过程是非阻塞的,通过轮询来判断内核中数据是否准备好。

IO multiplexing

IO multiplexing.jpg

  • 系统首先检查内核数据是否通过select准备就绪
  • 当内核数据一旦加载,系统调用 recvfrom加载内核态数据到用户态
  • 看似第一步和第二步是阻塞操作, 但是,select可以以非常低的成本同时处理多个文件处理(包括套接字)。

Signal driven IO

Signal driven IO.jpg

  • 首先注册回调函数在内核数据准备好后,通知我
  • 此时,系统能够摆脱阻塞状态(waiting内核数据),去干其他事情.
  • 阻塞调用recvfrom,加载内核态数据到用户态。

Asynchronous IO

Asynchronous IO.jpg

  • 理论上,这个模式是真正意义上的异步模型。之前的4种模型中,在二步,数据从内核态加载到用户态的过程都是同步操作的。
  • 在这个模式中,当系统加载文件时,只需要通过AIO_ Read注册一个回调来提醒当前系统,在内核态到用户态转换的时候。
  • 在这个进程中,系统可以处理其他事情,而不用等待(waiting 状态

总结和比较

compare.jpg

  • 上图是5种IO的总结和比较,理论上,越靠后越有效。
  • 前4种是同步synchronous IO,最后一种是异步asynchronous IO
  • 这里翻译之外,多说一句,异步解决的是阻塞问题,不解决计算能力问题,这一点就好比,你去挂号看病,虽然给你发了号,你可以干其他的去了,但是并不能解决医生看病的速度,该等还是要等。

Java中的各种模式对系统的调用

  1. 为什么NIO 放弃select 使用epoll
    • 因为select 是一种轮询模型,它会不断检查文件处理的状态。
    • 而epoll是回调模型,当文件准备完毕,它可以直接回调,这更有效。
  2. Java是否有真正的异步模型。
    • 在windows系统下,是的,由 IOCP来实现
    • 在Linux下,不是,在AIO层下,仍然是Linux的epoll