近期翻看centos IO模型的时候,外网找到一篇文章,感觉解释清晰,配图精美,现在以原文为主干,辅以自己的一些理解,介绍Linux IO模型。
原文地址:Five IO models of UNIX
五种UNIX IO模型
UNIX IO 模型和 Java IO模型的联系
- 五种Unix IO 模型
序号 | IO模型 | 中文翻译 |
---|---|---|
1 | blocking IO | 阻塞 |
2 | non blocking IO | 非阻塞 |
3 | IO multiplexing | 多路复用 |
4 | signal driven IO | 信号驱动模式 |
5 | asynchronous IO | 异步 |
- UNIX IO与JAVA IO 的关系
Java IO model | Unix IO model |
---|---|
BIO | Blocking IO |
NIO | IO multiplexing |
AIO | Asynchronous IO |
内核态与用户态
在聊IO模型之前,先要讲一下数据(进程)的用户态和内核态(user state and kernel state.)
我们将磁盘中的文件加载到内存中,系统会怎么作呢?
- 由于我们的所有程序与操作系统的内核交互,因此首先将文件从磁盘加载到内核,然后文件处于内核状态(kernel state)。
- 该文件从内核重新加载到内存,然后可以在内存中读取和写入应用程序。此时,该文件处于内核模式(kernel model)。
- 因此UNIX的五个IO模型的差异指出,这两个步骤的处理流程是不同的。
五种IO模式分别如下:
Blocking IO
-
这一步非常简单,首先系统调用recvfrom函数后,线程就会一直等待直到:
第一步是加载文件到内核模式
第二步是加载文件到用户模式 -
在linux中,默认情况下所有的socket都是blocking。它符合人们最常见的思考逻辑。
阻塞就是进程 "被" 休息
, CPU处理其它进程去了。
Non-Blocking IO
- 系统不断推送 recvfrom Poll 直到第一步完成,然后第二步,将数据由内核态转向用户态阻塞。
- 这里的非阻塞IO模式主要是指第一步,将数据加载到内核态。 这个过程是非阻塞的,通过轮询来判断内核中数据是否准备好。
IO multiplexing
- 系统首先检查内核数据是否通过
select
准备就绪 - 当内核数据一旦加载,系统调用 recvfrom加载内核态数据到用户态
- 看似第一步和第二步是阻塞操作, 但是,
select
可以以非常低的成本同时处理多个文件处理(包括套接字)。
Signal driven IO
- 首先注册回调函数在内核数据准备好后,通知我
- 此时,系统能够摆脱阻塞状态(waiting内核数据),去干其他事情.
- 阻塞调用recvfrom,加载内核态数据到用户态。
Asynchronous IO
- 理论上,这个模式是真正意义上的异步模型。之前的4种模型中,在二步,数据从内核态加载到用户态的过程都是同步操作的。
- 在这个模式中,当系统加载文件时,只需要通过
AIO_ Read
注册一个回调来提醒当前系统,在内核态到用户态转换的时候。 - 在这个进程中,系统可以处理其他事情,而不用等待(
waiting 状态
)
总结和比较
- 上图是5种IO的总结和比较,理论上,越靠后越有效。
- 前4种是同步
synchronous IO
,最后一种是异步asynchronous IO
。 - 这里翻译之外,多说一句,异步解决的是阻塞问题,不解决计算能力问题,这一点就好比,你去挂号看病,虽然给你发了号,你可以干其他的去了,但是并不能解决医生看病的速度,该等还是要等。
Java中的各种模式对系统的调用
- 为什么NIO 放弃select 使用epoll
- 因为select 是一种轮询模型,它会不断检查文件处理的状态。
- 而epoll是回调模型,当文件准备完毕,它可以直接回调,这更有效。
- Java是否有真正的异步模型。
- 在windows系统下,是的,由 IOCP来实现
- 在Linux下,不是,在AIO层下,仍然是Linux的epoll