当前位置:首页>Linux>C 语言 027 | Linux IO 多路复用深入:epoll 三种工作模式与实战

C 语言 027 | Linux IO 多路复用深入:epoll 三种工作模式与实战

  • 2026-10-08 11:26:49
C 语言 027 | Linux IO 多路复用深入:epoll 三种工作模式与实战
上一期讲了高并发服务器的三种经典模型。
这一期讲 epoll。

一、为什么 select 和 poll 性能不行

在讲 epoll 之前,我们先搞清楚为什么 select 和 poll 支撑不了高并发:

1. select 的三大硬伤

最大连接数限制:select 默认最大支持 1024 个文件描述符,虽然可以修改内核参数重新编译,但本质上是用数组存储 fd,连接数越大性能越差

全量拷贝:每次调用 select 都要把 fd 集合从用户态拷贝到内核态,连接数多了拷贝开销极大

全量遍历:每次有事件发生,都要遍历所有传入的 fd,才能知道哪个 fd 有事件,O (n) 的时间复杂度,连接越多越慢

2. poll 的改进与不足

poll 用链表存储 fd,去掉了 1024 的连接数限制,但全量拷贝和全量遍历这两个核心问题完全没解决,所以高并发下性能还是很差。

二、epoll 的核心设计:解决了什么问题

epoll 针对 select/poll 的三个硬伤,做了三个核心改进:

1. 红黑树存储 fd

epoll 在内核里用红黑树存储所有监听的 fd,增删改查都是 O (logn),没有最大连接数限制,一台普通服务器轻松支持几十万连接。

2. 只拷贝一次

调用 epoll_ctl 添加 fd 的时候,就把 fd 拷贝到内核里了,不需要每次 epoll_wait 都全量拷贝,只需要拷贝有事件的 fd 即可。

3. 就绪链表 + 回调机制

每个 fd 上有事件发生时,内核会自动把这个 fd 加入就绪链表,epoll_wait 只需要返回就绪链表里的 fd 就行,不需要遍历所有 fd,时间复杂度是 O (1),不管有多少连接,性能都不会下降。

三、epoll 三个核心函数详解

1. epoll_create:创建 epoll 实例

size:早期内核用来告诉内核要监听多少个 fd,现在内核用红黑树,这个参数已经没用了,只要填大于 0 的数就行

返回值:epoll 实例的文件描述符,用完要 close

2. epoll_ctl:管理 fd

epfd:epoll_create 返回的 fd

op:操作类型,三个可选值:

fd:要操作的文件描述符

event:要监听的事件,常用事件:

3. epoll_wait:等待事件

events:输出参数,用来存放有事件发生的 fd

maxevents:最多返回多少个事件,不能大于 epoll_create 时的 size

timeout:超时时间,-1 表示无限等待,0 表示不阻塞立即返回

返回值:有事件发生的 fd 数量

四、epoll 两种工作模式:LT vs ET 彻底讲透

这是面试的考点,一定要搞清楚。

1. 水平触发(Level Trigger,LT,默认模式)

触发规则:只要 fd 上还有未处理的数据,epoll_wait 就会一直通知你,直到你把数据处理完。
举个例子:客户端发了 1000 字节数据,你第一次只 read 了 500 字节,LT 模式下,下一次 epoll_wait 还会通知你这个 fd 可读,直到你把剩下的 500 字节读完。
优点:

编程简单,和 select/poll 行为完全一致

不容易丢数据,就算一次没读完,下次还会通知

出 bug 概率低,大部分开源项目默认用 LT

缺点:

事件通知次数多,高并发下会有一定的性能开销

2. 边缘触发(Edge Trigger,ET)

触发规则:只有 fd 的状态发生变化的那一刻才通知一次,不管数据有没有读完,之后不会再通知了。
举个例子:客户端发了 1000 字节数据,epoll 通知你一次,如果你只 read 了 500 字节,剩下的 500 字节不会再通知你,数据就丢了。
优点:

事件通知次数少,高并发下性能更高

减少了 epoll_wait 的调用次数,吞吐量更高

缺点:

编程复杂,非常容易丢数据

必须搭配非阻塞 IO,必须循环 read 直到 EAGAIN

3. 两种模式完整代码对比

LT 模式服务器(简单不易错)

ET 模式服务器(高性能,复杂)

踩坑点

ET 模式必须用非阻塞 IO:如果用阻塞 IO,read 会一直等数据,整个服务器就卡住了

ET 模式必须循环 read/write:只读一次一定会丢数据,这是 90% 的人都会犯的错

优先用 LT 模式:除非你确定要极致性能,否则 LT 模式足够用,出 bug 概率低很多

五、总结

select/poll 的硬伤:连接数限制、全量拷贝、全量遍历

epoll 的三大核心改进:红黑树、只拷贝一次、就绪链表回调

epoll 三个核心函数的参数与用法

LT 和 ET 两种模式的原理、优缺点、代码对比、踩坑点epoll 是网络编程的核心,也是后端面试的必考点,一定要亲手写代码跑一遍,理解两种模式的区别。

下一期用 epoll 实现一个完整的 HTTP 服务器。

大家有任何不懂的地方,或者敲代码遇到了 bug,都可以在评论区留言,我都会一一回复。如果这篇内容对你有帮助,别忘了点赞、在看、转发给身边同样在学 C 语言的朋友。我们下期见。

最新文章

随机文章