← 返回题目列表

五种 IO 模型是什么?它们有什么区别?

高频 中等 第 8 / 27 题 更新于 2026/07/28
IO模型阻塞IOIO多路复用异步IO网络编程

简化版

UNIX 下有五种 IO 模型:阻塞 IO、非阻塞 IO、IO 多路复用、信号驱动 IO、异步 IO。理解它们的钥匙是一次 IO 分两个阶段① 等待数据就绪(数据到达内核缓冲区)、② 把数据从内核拷贝到用户空间。前四种在阶段 ② 都要应用参与/等待,属于同步 IO;只有异步 IO(AIO)两个阶段都由内核完成、完事才通知应用,才是真正的异步

详细版

一次 IO 的两个阶段(以读数据 recvfrom 为例):

  1. 等待数据就绪:等数据从网络到达、进内核缓冲区;
  2. 数据拷贝:把数据从内核缓冲区拷贝到用户进程的缓冲区。

五种模型的区别,就在于这两个阶段各自阻塞不阻塞、由谁完成

模型阶段①等待数据阶段②拷贝数据同步/异步
阻塞 IO阻塞阻塞同步
非阻塞 IO不阻塞(轮询)阻塞同步
IO 多路复用阻塞在 select/epoll阻塞同步
信号驱动 IO不阻塞(信号通知)阻塞同步
异步 IO(AIO)不阻塞不阻塞(内核做)异步
  • 阻塞 IO:调 recvfrom,数据没好就一直挂起等,数据好了再拷贝、期间也在等,全程阻塞。最简单,但一个线程只能处理一个连接。
  • 非阻塞 IOrecvfrom 立即返回(没数据返回错误 EWOULDBLOCK),应用不停轮询问「好了没」。不阻塞但空轮询耗 CPU
  • IO 多路复用:用 select/poll/epoll 一次监听一大批 fd,哪个就绪了就处理哪个,用一个线程管理大量连接。阶段①阻塞在 select 上,就绪后仍要自己 recvfrom 拷贝。这是高并发服务器的主流。
  • 信号驱动 IO:注册一个信号处理函数,数据就绪时内核发 SIGIO 信号通知,应用再去读。实际用得少。
  • 异步 IO(AIO):应用发起 aio_read立即返回去干别的,内核把「等待 + 拷贝」全做完,再通知应用「数据已在你的缓冲区了」。应用完全不参与 IO,是真异步。

完整版教学

一、抓住核心:一次 IO 的两个阶段

理解五种 IO 模型,千万别去背表格,而要抓住那个统一的框架——一次 IO 分「等数据」和「拷数据」两个阶段

  • 阶段①(等待数据就绪):数据还没来,得等它从网络到达内核缓冲区;
  • 阶段②(数据拷贝):数据到了内核,要把它从内核空间拷贝到用户空间,应用才能用。

五种模型的所有差异,都是这两个阶段「谁来等、要不要阻塞、谁来拷贝」的不同组合。把这个框架记牢,五种模型就能自己推出来,不用死记。

二、阻塞 → 非阻塞 → 多路复用:一条优化主线

前三种模型其实是**逐步优化「一个线程处理多个连接」**的演进:

  • 阻塞 IO:一次 recvfrom 从头阻塞到尾。想同时处理多个连接,只能一连接一线程——连接一多,线程爆炸(这正是 C10K 问题,详见那道题)。
  • 非阻塞 IOrecvfrom 不阻塞了,没数据立即返回。理论上一个线程能轮询多个 fd,但空轮询(没数据也一直问)白白烧 CPU,效率低。
  • IO 多路复用:改进「轮询」——不再由应用一个个 fd 空问,而是把一大批 fd 交给内核(select/epoll),让内核一次性告诉你哪些就绪了。应用只处理就绪的 fd,一个线程高效管理成千上万个连接。这是高并发网络编程的基石。

所以多路复用不是凭空冒出来的,它是「非阻塞 IO + 由内核统一监控」的进化,解决了非阻塞轮询浪费 CPU 的问题。

三、为什么 IO 多路复用仍是「同步」的

一个极高频的追问:IO 多路复用到底是同步还是异步?答案是同步。

很多人以为「一个线程管好多连接、还不阻塞在单个 fd 上」就是异步,其实不是。关键看阶段②(数据拷贝)

  • select/epoll 帮你监控了阶段①(等数据就绪),就绪后它告诉你「fd X 可读了」;
  • 真正的数据拷贝(阶段②)还得你自己调 recvfrom 去做,而这一步是阻塞的——由应用发起、应用等待完成。

「数据拷贝要应用亲自参与、并等待其完成」,这就是同步的定义。所以 IO 多路复用是「同步非阻塞」的高效形式,但依然是同步 IO。只有连拷贝都交给内核的 AIO 才是异步。

四、只有 AIO 是真异步

异步 IO(AIO) 和前四种的本质区别是——两个阶段(等待 + 拷贝)全部由内核完成,应用完全不参与

  • 应用调 aio_read立刻返回,去干别的事;
  • 内核默默等数据、拷贝数据到应用缓冲区;
  • 全部完成后,内核主动通知应用「你要的数据已经在缓冲区里了,直接用」。

应用从头到尾没有为这次 IO 阻塞、也没参与拷贝——这才是「异步」。对比之下,前四种模型的阶段②都是应用同步地做/等的,所以都是同步 IO。

现实补充:Linux 的原生 AIO 长期不够成熟(对网络 IO 支持差),所以 Linux 高并发主流是「IO 多路复用(epoll)+ Reactor」这套同步方案;Windows 的 IOCP 是成熟的异步方案。新的 io_uring 正在改善 Linux 的异步 IO。

五、同步/异步 vs 阻塞/非阻塞:两个维度

这五种模型常和「同步/异步、阻塞/非阻塞」一起考,注意它们是两个不同维度(详见「同步异步阻塞非阻塞」那道题):

  • 阻塞/非阻塞:主要描述阶段①——发起调用后,数据没就绪时线程要不要挂起等;
  • 同步/异步:主要看阶段②——数据拷贝由应用参与/等待(同步),还是内核全包完再通知(异步)。

所以「非阻塞」不等于「异步」:非阻塞 IO 和多路复用都是「非阻塞地等 + 同步地拷」。真异步只有 AIO。

六、常见误区与追问

考点正确口径
阻塞 IO调用线程等待数据准备和拷贝完成
非阻塞 IO立即返回,应用轮询或配合事件通知
IO 多路复用一个线程等待多个 fd 的就绪事件
blocking read:
wait data -> copy data -> return

nonblocking read:
no data -> EAGAIN

multiplexing:
select/epoll waits many fds -> read ready fds

比较 IO 模型时要拆成两段:等数据就绪,以及把数据从内核拷贝到用户态。

  • 误区:非阻塞 IO 就等于异步 IO。 非阻塞 read 只是立即返回,真正的数据拷贝通常仍由应用线程发起。
  • 误区:IO 多路复用能让单个请求处理更快。 它主要提升大量连接的管理效率,不一定降低单个请求的业务处理时间。
  • 误区:阻塞 IO 一定不能用于生产。 连接数少、线程池可控、业务简单时阻塞 IO 仍然可用。
  • 追问:同步和异步看什么? 看调用方是否需要自己参与结果等待和数据拷贝完成。
  • 追问:阻塞和非阻塞看什么? 看调用发出后当前线程是否被挂起等待。
  • 追问:Linux AIO/io_uring 和 epoll 有何差别? epoll 通知 fd 就绪,io_uring 更接近提交操作并异步收完成事件。

七、加强记忆

五种 IO 模型的钥匙是「一次 IO = 等待数据就绪 + 数据拷贝到用户空间」两阶段。阻塞 IO(全程阻塞)、非阻塞 IO(轮询等、拷贝阻塞)、IO 多路复用(select/epoll 统一监控、就绪后自己拷)、信号驱动 IO(信号通知)——这四种阶段②都要应用参与,都是同步;只有异步 IO(AIO,等待+拷贝全内核做完再通知)是真异步。前三种是「一线程处理多连接」的优化主线,多路复用是主流。