epoll 源码路径:从epoll_ctl 到ep_poll 的就绪唤醒

select 在高连接数下 CPU 飙高、或 epoll_wait 空转/漏事件:需要看清内核 就绪链表与回调 ,而不是只背 API。本文沿 epoll_create1 → epoll_ctl(ADD) → 文件就绪回调 → ep_poll/epoll_wait 走通 fs/eventpoll.c 主路径,便于用 strace/perf 验证。

源码锚点

路径 作用
fs/eventpoll.c do_epoll_create、do_epoll_ctl、ep_poll、ep_send_events
include/uapi/linux/eventpoll.h epoll_event、EPOLLIN/EPOLLET 等用户 UAPI
include/linux/eventpoll.h 内核侧声明
fs/select.c 经典 select/poll 实现(对比用)
net/ipv4/tcp.c 等 socket 通过 ->poll / wait queue 上报就绪

核心用户结构:

c 复制代码
/* include/uapi/linux/eventpoll.h */
struct epoll_event {
	__poll_t events;
	__u64 data;
};

内核侧(概念):每个 epoll 实例有红黑树存监控项(epitem),就绪队列串起「已就绪」项;目标文件通过 epoll 回调挂到其 wait queue。

c 复制代码
/* 用户态典型调用 */
int epfd = epoll_create1(EPOLL_CLOEXEC);
epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &ev);
n = epoll_wait(epfd, events, maxevents, timeout);

调用链

text 复制代码
epoll_create1(flags)
  → do_epoll_create
      → 分配 eventpoll 对象,匿名 inode + file

epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &event)
  → do_epoll_ctl
      → 红黑树插入 epitem(key=fd 对应的 file)
      → ep_insert
          → 对目标 file 调用 f_op->poll,挂 epoll 回调
          → 若已就绪 → 链入 rdllist(就绪链表)

目标 fd 数据到达 / 可写
  → sock 等子系统 wake_up
      → epoll 回调 ep_poll_callback
          → epitem 进入就绪链表,唤醒正在 ep_poll 等待的进程

epoll_wait(epfd, ...)
  → do_epoll_wait → ep_poll
      → 若就绪链表空则睡眠至超时/唤醒
      → ep_send_events 拷贝到用户 events[]

水平触发(默认)vs 边沿触发(EPOLLET):

text 复制代码
LT:只要条件仍满足,每次 wait 都可再次报到
ET:仅状态变化时报到;必须一次读到 EAGAIN,否则可能饿死

重点知识

1. 为何 epoll 能撑高并发

select/poll 每次把整张 fd 集合从用户拷进内核并线性扫描;epoll 用红黑树索引监控集,就绪用链表,热路径只处理就绪子集 。连接数上万时差异明显。代价是:每 fd 需先 epoll_ctl,且语义(尤其 ET)要正确。

2. data 字段是用户私有,内核不解释

epoll_event.data 常存 fd 或对象指针。关闭 fd 前必须 EPOLL_CTL_DEL(或依赖 DUP/CLOEXEC 生命周期约定),否则可能出现 野事件(fd 号复用后事件张冠李戴)。

3. ET + 非阻塞是标配

边沿触发下,若 read 未读到 EAGAIN 就去 epoll_wait,剩余数据可能不再通知。生产网络库普遍:SOCK_NONBLOCK + EPOLLET + 循环读到 EAGAIN。LT 更易用,但高活跃 fd 会重复报到。

4. 配置与观测

bash 复制代码
# 系统调用轨迹
strace -e epoll_create1,epoll_ctl,epoll_wait ./your_server
# 句柄与上限
ulimit -n
cat /proc/sys/fs/epoll/max_user_watches   # 监控项上限(若存在)
# 性能
perf record -e syscalls:sys_enter_epoll_wait -a -- sleep 5
ss -s                                    # 连接规模旁证

对比实验:同连接数下分别用 poll 与 epoll 看 CPU;或故意 ET 不读干,观察是否「假死」。

5. 设计意图与踩坑

  • 意图:把「谁就绪」从 O(n) 扫描变成就绪队列消费。
  • 坑 :epoll_wait 的 maxevents 过小,一轮取不完,逻辑上要循环到取尽或配合 LT。
  • 坑 :把阻塞 fd 塞进 epoll 却假设一定非阻塞 → read 卡死工作线程。
  • 坑 :EPOLLONESHOT 用后忘记 EPOLL_CTL_MOD 重新武装。
  • 坑:在多线程间乱共享同一 epfd 无同步(内核允许,但用户状态机要自己保证)。

Checklist

  • 能指出 do_epoll_ctl / ep_poll 位于 fs/eventpoll.c,并口述就绪唤醒链
  • 说明 LT 与 ET 差异,并在 ET 下写到「读至 EAGAIN」的循环
  • strace 看到 create/ctl/wait;关闭连接路径有 DEL 或等价生命周期管理
  • 对比理解 select/poll 全量扫描与 epoll 就绪链表的复杂度差异
  • 检查 ulimit -n 与 max_user_watches,避免 EMFILE/监控项耗尽
  • 使用 EPOLLONESHOT 时有明确的重新 MOD 路径
相关推荐
发光小北7 小时前
四路can转WIFI在现场应用中有什么问题?
网络协议
IvorySQL8 小时前
PostgreSQL 日报|修复截断 zstd 备份检测(10 月 9 日)
数据库·postgresql
不像程序员的程序媛8 小时前
检测网络的命令mtr详解
网络
刘胡子大叔8 小时前
SQL 脚本的导入顺序
数据库·sql
天启HTTP8 小时前
爬虫防封核心原理:IP轮换机制与风控规避逻辑
linux·服务器·网络·爬虫·tcp/ip
代码什么用8 小时前
Spring对IoC的实现
数据库·spring
运维行者_8 小时前
网络性能监控怎么做?从自动发现到根因分析的4个环节
运维·服务器·网络·人工智能·支持向量机
hz5678910 小时前
涉密视频会议设备配置指南:终端、音视频采集与配套设施选型
服务器·网络·数据库·安全·实时音视频·信息与通信·智能硬件
广州浮点FLOATLIC11 小时前
许可证服务器迁移后软件打不开:研发 IT 怎样定位连接问题
linux·服务器·数据库
程序员Sunday11 小时前
MySQL 为什么使用 B+ 树索引?把范围查询、回表和覆盖索引连起来
数据库·mysql