Rust in Everything
基于 mio 1.2.4

mio 源码解析

全卷只对着一个版本写:

crate版本tagcommit日期
mio1.2.4v1.2.479d14172026-10-03

mio 是 tokio 下面那一层。tokio 卷的 IO 驱动讲的是 tokio 怎么用 mio:反应器每轮调一次 Poll::poll,按 token 找回 ScheduledIo、置就绪位、唤醒任务;从 TcpStream::read 到挂起讲任务读到 WouldBlock 之后怎么挂起。那几页把 mio 当成一个黑盒:「注册一下」「poll 一下」。这一卷打开这个黑盒,看这两个动作各变成哪几个系统调用,以及 Linux、macOS、Windows 和其他 Unix 上为什么不一样。

mio 只做一件事:把「这个 fd 什么时候可读、可写」从操作系统那里取回来。全卷沿两条线展开:

  • 注册主线(一次 register):TcpListener::bind 得到一个非阻塞 socket,包在 IoSource<T> 里;Registry::register(&mut source, Token, Interest) 经 event::Source::register 落到平台的 Selector。Linux 上是一次 epoll_ctl(EPOLL_CTL_ADD),标志 EPOLLIN|EPOLLRDHUP|EPOLLET,Token 原样放进 data;macOS 上是一次 kevent,关心读就提交一个 EVFILT_READ、关心写就提交一个 EVFILT_WRITE,都带 EV_CLEAR,Token 放进 udata。mio 自己不维护 fd 到 token 的表。
  • 事件主线(一次 poll):Poll::poll(&mut events, timeout) 落到 epoll_wait 或 kevent,内核把就绪项直接写进 Events 里的 Vec;用户遍历 Event,token()、is_readable() 这些方法只是在解释平台的标志位;然后对 source 做非阻塞 I/O,直到 WouldBlock,再回去 poll。Waker 是这条线上的旁路:另一个线程 wake(),让阻塞中的 poll 带着 waker 的 token 返回。

两条线每一步对应的源码和页面,见怎么读这一卷。

flowchart TB
  intro[00 导读] --> overview[01 全景]
  overview --> core[02 核心类型]
  core --> plat[03 平台实现]
  plat --> net[04 网络类型]
  net --> design[05 设计取舍]
  design --> pit[06 常见坑]
  pit --> site[07 在本站中]
  site --> hands[08 动手]

实验环境

系统调用级别的说法都用真实程序证明。macOS(kqueue)上直接 cargo run;Linux(epoll)上把程序交叉编译成 aarch64 musl 静态二进制,放进 Apple container 里的 Alpine(内核 6.12.28)运行,用 strace 记下每一个 epoll_ctl、epoll_pwait、eventfd2、accept4。每个实验都注明平台;同一个程序在两个平台上结果不同的地方,两边的输出都贴出来。

各部分

  1. 00 导读:锁定的版本,1.2.1(本站锁的版本)到 1.2.4 的每一处改动落在哪个平台;两条主线、阅读路线、实验环境;src/ 的平台分层、三个 feature 和 sys/shell、每个平台用哪个 selector、测试与 CI。
  2. 01 全景:就绪模型和完成模型,mio 为什么选「就绪 + 边沿触发」,不做什么;一个最小的事件循环在 macOS 和 Linux 上各跑一次。
  3. 02 核心类型:本卷重点。Poll 与 Registry,Token、Interest 与 Event,Source 与 IoSource,Waker。
  4. 03 平台实现:epoll、kqueue、poll(2) 后备实现,以及 Windows 上怎么用 IOCP 和 AFD 模拟出就绪通知。
  5. 04 网络类型:TcpListener / TcpStream 的非阻塞细节,UDP、Unix 域套接字和 pipe。
  6. 05 设计取舍:把各章的取舍汇总到一起。
  7. 06 常见坑:用报错原文或现象做标题,每个都先跑出最小复现。
  8. 07 在本站中:本站不直接用 mio,全部经由 tokio;生产(Linux)和开发(macOS)上同一个程序的系统调用对照;本站锁的 1.2.1 与本卷的差异。
  9. 08 动手:不用 mio,直接用 libc 写一个只支持 epoll 的迷你 mio。

从「导读」开始。只想会用 mio 的读者,读 01 全景、02 核心类型、04 网络类型和常见坑;想读懂实现的,按顺序读。02 是全卷的重点:两条主线都从那里的四个类型出发,03 再分平台往下走。

💬 关联讨论

发起讨论
加载中...