select 是最古老的 I/O 多路复用 机制。它允许进程同时监控多个 文件描述符 (FD) 的就绪状态。

核心逻辑

select 的核心是使用 位图 (Bitmap) 来管理 FD 集合。

  1. 位图映射:用一个固定长度的位图(通常 1024 位)表示监控的 FD。第 位为 1 表示监控 FD
  2. 全量拷贝:每次调用都需要将整个位图从用户态拷贝到内核态。
  3. 原地修改:内核发现就绪 FD 后,会直接修改传入的位图来标记就绪状态。
  4. 线性触发:内核返回后,用户进程必须 遍历整个位图才能找到具体哪个 FD 就绪。
sequenceDiagram
    participant U as 用户进程
    participant K as 内核 (Kernel)
    
    U->>U: 初始化 fd_set (位图)
    U->>K: select(max_fd, &readset, ...)
    Note over K: 拷贝位图到内核
    Note over K: 线性扫描所有 FD
    K-->>K: 发现数据就绪
    Note over K: 修改位图,仅保留就绪位
    K->>U: 返回就绪数量
    Note over U: O(N) 遍历位图找出就绪 FD

致命缺陷

  • 1024 限制:受限于 FD_SETSIZE,单个进程最多只能监控 1024 个 FD。
  • 不可复用:内核会原地修改位图,导致下次调用前必须重新初始化。
  • 性能瓶颈
    • 拷贝开销:每次调用都要全量拷贝位图。
    • 扫描开销:内核和用户态都需要 遍历,FD 越多越慢。