3.1 前置讨论

1 为什么从未见到 disk io 使用 epoll?

在 Linux 的世界中,一切皆文件的思想贯彻始终。然而文件也是有各种类型的。

类型标识符 文件类型 描述 典型示例
- Regular File(普通文件) 存储在磁盘上的标准数据文件(文本、二进制、压缩包等) /etc/passwd, app.exe, data.zip
b Block Device(块设备) 按数据块访问的设备(有缓冲,支持随机访问) /dev/sda, /dev/nvme0n1
s Socket(套接字) 进程间通信的端点(网络或本地) /run/docker.sock

本地文件的 IO 操作的是 Regular File,它永远都是可就绪的。

POSIX 允许:普通文件的 O_NONBLOCK 可能被忽略(多数实现中无效)1。因此,使用对其使用 epoll 没有意义。

普通文件的读写操作在传统内核中总是就绪(从内核视角看,磁盘 I/O 的阻塞发生在底层驱动,而非文件描述符层面)。即使设置 O_NONBLOCK,read()/write() 仍会因等待磁盘操作而阻塞。

继续阅读相关文章2

注:有些高性能存储系统会实现自己的通信网络栈,涉及到网络通信,自然可能使用 epoll 技术。本节只讨论本地文件 IO。

2 我真的有必要使用异步 IO 吗?

工程没有银弹。笔者认为,只有明确需要处理超高负载、延迟要求较低的系统,以及使用同步 I/O 无法榨干硬件性能;或者计算任务混合部署时,才需要引入异步 IO。

同时,开发者需要意识到,异步 I/O ≠ 更高性能。若系统已经达到了硬件能力,更换 IO 模型也几乎不可能令吞吐翻倍,往往是需要考虑更合理的 IO 系统模式设计。

异步 I/O 打破了天然人类思维的 “顺序流程”。整个系统可能需要引入 async,以及回调/状态机/协程;需要引入智能指针在整个异步过程中管理 IO buf 的生命周期等等。这些都增大了调试、理解的难度。(试想一下在 gdb 栈和日志中调查一个充满了回调系统的非预期行为…)。

  graph TD
    A[存储系统需求]
    A --> C[优先使用异步I/O]
    A --> D[同步I/O+优化可接受]
    C --> E[高并发>10K QPS]
    C --> F[延迟要求<1ms]
    C --> G[硬件IOPS>100K]
    D --> H[吞吐<1GB/s]
    D --> I[单线程即可满足]