0%

IO模型

IO模型

linux中有几种IO模型,如select、poll、epoll,这几个分别是什么呢?

select 模型

  • 工作流程:
    1. 首先创建一个文件描述符(fd)集合,添加需要监测的 socket 句柄。
    2. 调用select()函数,将 fd 集合从用户态拷贝到内核态。
    3. 内核遍历所有 fd,检查是否有 IO 事件(如可读、可写)就绪。
    4. 若有就绪事件,内核修改 fd 集合标记就绪的 fd,然后将集合拷贝回用户态。
    5. 用户态遍历 fd 集合,找到就绪的 fd 并处理。
  • 缺点:
    • 最大 fd 限制:默认 1024(可通过修改内核参数调整,但不推荐)。
    • 效率低:每次调用都需拷贝 fd 集合,且内核和用户态都要遍历所有 fd,高并发下开销剧增。
    • 水平触发(LT):若就绪事件未被处理,下次调用仍会通知,可能导致重复处理。

poll 模型

  • 改进点:
    • 用链表存储 fd 集合,理论上无最大连接数限制,解决了 select 的 fd 数量瓶颈。
  • 与 select 的共性问题:
    • 仍需在用户态和内核态之间拷贝 fd 集合,高并发时拷贝开销大。
    • 内核和用户态都需遍历所有 fd,效率随 fd 数量增加而下降。
    • 同样采用水平触发(LT)机制。

epoll 模型

  • 核心改进:
    • 事件驱动:不再轮询,而是为每个 fd 注册回调函数,当 IO 事件就绪时,内核主动触发回调,将就绪 fd 加入就绪队列。
    • 减少拷贝:fd 集合只需在初始化时从用户态拷贝到内核态(通过epoll_ctl()),后续无需重复拷贝。
    • 高效遍历:用户态通过epoll_wait()直接获取就绪 fd 列表,无需遍历所有 fd。
  • 触发模式:
    • 水平触发(LT):默认模式,若就绪事件未处理,下次仍会通知(兼容 select/poll 的使用习惯)。
    • 边缘触发(ET):仅在 fd 状态从 “未就绪” 变为 “就绪” 时通知一次,需一次性处理完所有数据,效率更高,但编程复杂度增加。
  • 优势场景:高并发、长连接场景(如服务器处理大量客户端请求),性能远超 select 和 poll。

三者对比表格

特性 select poll epoll
最大 fd 限制 1024(可修改) 无(链表存储) 无(理论上取决于系统内存)
fd 集合拷贝 每次调用拷贝 每次调用拷贝 仅初始化时拷贝
遍历方式 轮询所有 fd 轮询所有 fd 直接获取就绪 fd 列表
触发模式 水平触发(LT) 水平触发(LT) LT/ET(支持两种)
高并发效率 低(O (n)) 低(O (n)) 高(O (1))
适用场景 连接数少且固定 连接数中等 高并发、大连接数

总结来说,epoll 是 Linux 下高性能 IO 模型的首选,尤其在高并发场景下优势明显;而 select 和 poll 由于效率限制,更多用于简单场景或兼容性需求。

欢迎关注我的其它发布渠道