Rust in Everything
基于 mio 1.2.4

平台实现

核心类型一部分讲的那一层,最后都转到一个 sys::Selector。这一部分把各个 Selector 打开来看:它们对外的方法一样(new、try_clone、select、register、reregister、deregister),对内用的系统调用和要补的工作差别很大。sys/unix/mod.rs 按 target_os 用 cfg_attr(path = ...) 挑出其中一个文件编译进来,规则见源码地图。

四章按「原生支持边沿触发的,到要自己模拟的」排列:

  1. epoll:Linux、Android、illumos、Redox 共用的 246 行。Selector 只有一个 epoll fd;三个注册方法是 EPOLL_CTL_ADD / MOD / DEL,掩码总带 EPOLLET,关注可读时加 EPOLLRDHUP(不加的话对端 FIN 和普通数据都只是 EPOLLIN);MOD 会立刻重新检查就绪状态;strace 里的 epoll_pwait 是 aarch64 上 musl 对 epoll_wait 的实现。
  2. kqueue:macOS、其他 Apple 平台和四个 BSD。一个 fd 的读、写各是一个过滤器,register 一次 kevent 按关注的方向提交 changelist 项,EV_RECEIPT 让每一项单独带回错误;EV_CLEAR 是 kqueue 的边沿触发;读写事件分两条返回,同一个 token 会在一批 Events 里出现两次;Waker 用 EVFILT_USER。
  3. poll(2) 后备:没有 epoll / kqueue 的 Unix,或用 --cfg mio_unsupported_force_poll_poll 强制。poll(2) 是水平触发,mio 在用户态维护关注集合,靠 registered_io_source.rs 在 do_io 遇到 WouldBlock 时重新武装,用一个内部 waker 叫醒正在 poll 的线程来接受跨线程注册。
  4. Windows:IOCP 与 AFD:IOCP 是完成模型,mio 对每个 socket 发起一个 IOCTL_AFD_POLL,把它的完成当作就绪事件;AfdGroup、SockState 管理这些异步操作。只讲机制,不在 Windows 上跑实验。
flowchart TB
  sel["sys::Selector"] --> ep["epoll:Linux 等"]
  sel --> kq["kqueue:Apple 与 BSD"]
  sel --> pl["poll:其他 Unix"]
  sel --> win["IOCP + AFD:Windows"]
  ep --> et["原生边沿触发"]
  kq --> et
  pl --> emu["用户态模拟边沿"]
  win --> emu

Solaris 的 event ports(selector/event_ports.rs)和 WASI(sys/wasip1)不单独成章:前者和 poll(2) 后备共用 registered_io_source.rs 的重新武装逻辑,后者只用于 target_env = "p1" 的 WASI,别的 WASI 目标走 Unix 那一套代码和 poll(2) 后备。

💬 关联讨论

发起讨论
加载中...