【Linux】五种IO模型与非阻塞IO

Linux 系统下有 五种经典 IO 模型,这是网络编程和高并发服务器开发中必须掌握的核心知识(尤其在 Redis、Nginx、Netty 等场景中反复出现)。
2026 年视角下,io_uring 已成为异步 IO 的主流趋势,但面试和基础理解仍以这五种模型为主。

一、五种 IO 模型一览表(最清晰对比)

模型序号模型名称是否阻塞(进程视角)是否同步(数据拷贝阶段)内核是否帮忙拷贝到用户缓冲区典型系统调用适用场景效率排序(1最高)
1阻塞式 IOread / recv简单单连接、低并发★☆☆☆☆
2非阻塞式 IO否(轮询)read + EAGAIN 轮询极少数场景(几乎不用)★★☆☆☆
3IO 多路复用是(select/poll/epoll)select / poll / epoll高并发网络服务器(主流)★★★★☆
4信号驱动式 IOsigaction + SIGIO极少用(历史遗留)★★★☆☆
5异步 IO(AIO / io_uring)是(内核完成拷贝)aio_read / io_uring_submit极致性能、高吞吐磁盘/网络★★★★★

关键区分点(面试最常问):

  • 阻塞 vs 非阻塞:调用 read/recv 时,数据没准备好,进程是否挂起(阻塞态)
  • 同步 vs 异步:数据从内核缓冲区 → 用户缓冲区的拷贝阶段,谁负责?(同步=用户进程负责拷贝,异步=内核负责拷贝)

→ 前四种都是同步 IO(用户进程要自己把数据从内核 copy 到用户空间)
→ 第五种才是真正的异步 IO(内核全部搞定,包括拷贝,用户进程只收通知)

二、非阻塞 IO 详细讲解(最容易混淆的部分)

非阻塞 IO 的本质
把 socket / 文件描述符 设置为 O_NONBLOCK 后,read/recv 等调用不会让进程睡眠,而是立刻返回。

  • 数据没准备好 → 返回 -1 + errno = EAGAIN / EWOULDBLOCK
  • 数据准备好 → 正常返回读取的字节数

代码示例(最经典写法)

#include <fcntl.h>
#include <unistd.h>
#include <errno.h>

// 设置非阻塞
int flags = fcntl(fd, F_GETFL, 0);
fcntl(fd, F_SETFL, flags | O_NONBLOCK);

// 循环读取(轮询)
while (1) {
    ssize_t n = read(fd, buf, sizeof(buf));
    if (n > 0) {
        // 读到数据,处理
    } else if (n == -1) {
        if (errno == EAGAIN || errno == EWOULDBLOCK) {
            // 没数据,继续轮询(或 sleep(小) / 其他业务)
            // 这里就是 CPU 空转的代价!
        } else {
            // 真正错误
            perror("read error");
            break;
        }
    } else {
        // n == 0,对端关闭
        break;
    }
}

非阻塞 IO 的致命缺点(为什么生产几乎不用纯非阻塞):

  1. CPU 空转:轮询消耗 100% CPU(尤其多连接时灾难)
  2. 无法处理大量连接:每个 fd 都需要一个循环轮询
  3. 业务逻辑复杂:代码全是嵌套的 while + errno 判断,可读性极差

结论
纯非阻塞 IO 几乎只用于极少数特殊场景(如单连接、已知数据很快到达)。
高并发场景必须结合 IO 多路复用(select/poll/epoll)使用。

三、五种模型流程对比图(文字版)

1. 阻塞 IO
用户进程 → read() → 阻塞等待 → 数据就绪 → 内核拷贝到用户缓冲区 → 返回

2. 非阻塞 IO
用户进程 → read() → 立刻返回 EAGAIN → 轮询 → ... → 数据就绪 → read()成功 → 拷贝 → 返回

3. IO 多路复用(以 epoll 为例)
用户进程 → epoll_wait() → 阻塞等待事件 → 内核通知哪些 fd 就绪 → 用户 read() → 拷贝 → 返回

4. 信号驱动 IO
用户进程 → sigaction(SIGIO) → 继续干活 → 数据就绪 → 内核发信号 → 信号处理函数 read() → 拷贝

5. 异步 IO(io_uring / POSIX AIO)
用户进程 → io_submit() → 立刻返回 → 继续干活 → 内核完成全部(读+拷贝) → 通知(或轮询完成队列)

四、2026 年生产视角总结(面试 + 实际选型)

场景推荐模型主流实现备注(2026)
简单单线程阻塞服务器阻塞 IOread/accept教学用,生产已淘汰
高并发网络服务器IO 多路复用epoll(Linux) / kqueue(BSD/macOS)Nginx、Redis、Netty 默认
极致性能网络 + 磁盘异步 IOio_uringRedis 7+、Nginx 实验支持、liburing
历史遗留或特殊信号场景信号驱动 IOSIGIO几乎不用,容易丢事件
轮询单个 fd(很少见)非阻塞 IOO_NONBLOCK + while仅作理解,生产不推荐

一句话记忆口诀(超好背):

阻塞傻等鱼上钩,非阻塞自己一直问,多路复用请渔夫,信号驱动鱼咬钩喊你,异步渔夫全包办。

如果你想看具体代码实现(C / Python epoll / io_uring 入门),或者想对比 select/poll/epoll 的性能差异,告诉我,我可以继续展开。

文章已创建 3996

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

相关文章

开始在上面输入您的搜索词,然后按回车进行搜索。按ESC取消。

返回顶部