1.服务端I/O流程
磁盘I/O
- 定义:服务端与本地存储介质(如 HDD、SSD)之间的数据读写操作,是服务与本地持久化数据交互的核心方式。
- 典型场景 :
- 读取静态资源(如 HTML、图片、视频文件)
- 写入应用日志、用户数据、缓存快照
- 数据库的持久化存储(如 MySQL 的 InnoDB 数据文件)
- 核心特点 :
- 速度远低于内存访问,是服务性能的常见瓶颈
- 通常由内核的文件系统层(VFS)统一管理,应用通过系统调用(如
read()/write())触发 - 可通过页缓存(Page Cache)、Direct I/O 等方式优化性能
网络I/O
- 定义:服务端通过 TCP/IP 协议栈与客户端或其他服务进行数据收发的过程,是分布式系统的基础交互方式。
- 典型场景 :
- 接收 HTTP/HTTPS 请求、返回响应
- 微服务之间的 RPC 调用(如 gRPC、Dubbo)
- 实时通信(如 WebSocket、消息队列消费)
- 核心特点 :
- 涉及用户态与内核态的多次切换,以及数据在协议栈中的拷贝
- 受网络带宽、延迟、丢包等因素影响,不确定性更高
- 是高并发服务设计中最需要优化的环节
2.I/O模型
2.1 I/O 模型相关核心概念
2.1.1 阻塞 vs 非阻塞
- 阻塞 I/O:应用发起 I/O 调用后,线程会被操作系统挂起(进入睡眠状态),直到 I/O 操作完成(数据就绪并拷贝到用户空间)才返回继续执行。
- 非阻塞 I/O :应用发起 I/O 调用后,操作系统会立即返回。如果数据未就绪,返回
EAGAIN/EWOULDBLOCK错误;应用需要反复调用(轮询)直到数据就绪。
2.1.2 同步 vs 异步
- 同步 I/O:应用程序需要主动等待或检查 I/O 操作的完成状态,数据从内核空间拷贝到用户空间的过程由应用程序发起并等待。
- 异步 I/O:应用程序发起 I/O 操作后立即返回,后续由内核完成数据读取和拷贝,并在完成后主动通知应用程序,应用全程不参与等待。
2.1.3 用户态与内核态
- I/O 操作的本质是硬件与内存之间的数据传输,这一过程必须由内核完成。
- 应用程序(用户态)通过系统调用(如
read()、recvfrom())触发内核态操作,内核完成后将结果返回给用户态。 - 数据在用户态和内核态之间的拷贝是 I/O 性能开销的重要来源。
3.网络I/O模型
阻塞型、非阻塞型、信号驱动型、异步、复用型
3.1.阻塞型I/O模型

- 工作流程 :
- 应用调用
recvfrom(),内核开始等待网络数据(阻塞阶段)。 - 数据到达后,内核将数据从内核缓冲区拷贝到用户缓冲区(拷贝阶段)。
- 拷贝完成后,
recvfrom()返回,应用继续处理数据。
- 应用调用
- 特点 :
- 两个阶段(等待数据、拷贝数据)全程阻塞,线程无法处理其他任务。
- 实现简单,逻辑清晰,易于调试。
- 适用场景 :
- 并发量极低的场景(如内部工具、单用户应用)。
- 对开发效率要求高于性能要求的场景。
3.2.非阻塞型I/O模型

- 工作流程 :
- 应用将 socket 设置为非阻塞模式,调用
recvfrom()。 - 如果数据未就绪,内核立即返回
EAGAIN错误,应用不阻塞。 - 应用反复(轮询)调用
recvfrom(),直到数据就绪。 - 数据就绪后,内核将数据拷贝到用户空间,
recvfrom()返回成功。
- 应用将 socket 设置为非阻塞模式,调用
- 特点 :
- 等待数据阶段不阻塞,但拷贝数据阶段仍阻塞。
- 应用需要主动轮询,消耗大量 CPU 资源。
- 适用场景 :
- 需要避免线程阻塞,但并发量不高的场景。
- 通常作为 I/O 多路复用的基础组件,而非独立使用。
3.3.信号驱动式I/O模型

- 工作流程 :
- 应用通过
sigaction()系统调用注册一个信号处理函数。 - 应用发起 I/O 请求后立即返回,继续处理其他任务。
- 当数据就绪时,内核向应用发送
SIGIO信号。 - 应用收到信号后,在信号处理函数中调用
recvfrom()完成数据拷贝。
- 应用通过
- 特点 :
- 等待数据阶段不阻塞,应用被动接收通知。
- 拷贝数据阶段仍阻塞。
- 信号处理机制复杂,在高并发下容易丢失信号或产生竞态条件。
- 适用场景 :
- 对实时性要求较高,但并发量不大的场景。
- 现代高并发服务中已很少使用,逐渐被 I/O 多路复用替代。
3.4.异步I/O模型

- 工作流程 :
- 应用调用
aio_read()等异步 API,提交 I/O 请求和用户缓冲区地址。 - 应用立即返回,继续处理其他任务。
- 内核等待数据就绪,并主动将数据拷贝到应用提供的用户缓冲区。
- 拷贝完成后,内核通过信号或回调函数通知应用 I/O 完成。
- 应用调用
- 特点 :
- 等待数据和拷贝数据两个阶段都不阻塞。
- 应用完全不参与 I/O 过程,是真正的 "异步"。
- 实现复杂,对操作系统和编程语言支持要求高。
- 适用场景 :
- 追求极致性能和低延迟的高并发服务。
- 大量 I/O 密集型任务的场景(如分布式存储、大数据处理)。
3.5.多路复用I/O模型

- 工作流程 :
- 应用创建多个 socket,并将它们注册到一个多路复用器(如
select()、poll()、epoll())中。 - 应用调用多路复用器,阻塞等待任意一个 socket 数据就绪。
- 当有数据就绪时,多路复用器返回,应用遍历就绪列表,逐个处理数据。
- 应用创建多个 socket,并将它们注册到一个多路复用器(如
- 特点 :
- 单个线程可以同时管理多个连接,极大提升了并发能力。
- 等待数据阶段阻塞在多路复用器上,拷贝数据阶段仍阻塞。
- 是目前高并发网络服务(如 Nginx、Redis)的主流选择。
3.6.五种I/O模型对比
| 模型 | 等待数据阶段 | 数据拷贝阶段 | 应用程序感知 | 代表技术 | 核心优势 | 核心劣势 |
|---|---|---|---|---|---|---|
| 阻塞 I/O | 阻塞 | 阻塞 | 全程等待 | recvfrom() |
实现简单 | 并发能力极低,线程资源浪费严重 |
| 非阻塞 I/O | 不阻塞(返回错误) | 阻塞 | 主动轮询 | 非阻塞 socket | 不阻塞线程 | CPU 轮询开销大 |
| 信号驱动 I/O | 不阻塞(等待信号) | 阻塞 | 被动通知 | SIGIO |
无需轮询 | 信号处理复杂,易丢失 |
| 异步 I/O | 不阻塞 | 不阻塞 | 内核完成后通知 | aio_read()、io_uring |
完全异步,性能最优 | 实现复杂,兼容性差 |
| I/O 多路复用 | 阻塞在多路复用器 | 阻塞 | 批量监听 | select/poll/epoll |
单线程管理多连接,并发高 | 仍需同步处理就绪事件 |