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

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

源码锚点

路径 作用
fs/eventpoll.c do_epoll_createdo_epoll_ctlep_pollep_send_events
include/uapi/linux/eventpoll.h epoll_eventEPOLLIN/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                                    # 连接规模旁证

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

5. 设计意图与踩坑

  • 意图:把「谁就绪」从 O(n) 扫描变成就绪队列消费。
  • epoll_waitmaxevents 过小,一轮取不完,逻辑上要循环到取尽或配合 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 -nmax_user_watches,避免 EMFILE/监控项耗尽
  • 使用 EPOLLONESHOT 时有明确的重新 MOD 路径
相关推荐
简单Janeee16 分钟前
[Vue 3 从零到上线]-第九篇:更美更强——引入 UI 库与网络请求 (Axios)
网络·vue.js·ui
学习星球19 分钟前
空天地一体化网络(NTN)深度解析:从Starlink D2C到3GPP NTN,卫星直连手机是如何实现的?
网络·人工智能·算法·智能手机·php
小蒜学长21 分钟前
基于SpringBoot + Vue的智能健身房管理系统的设计与实现(代码+数据库+LW)
java·数据库·vue.js·spring boot·后端
芦柑46430 分钟前
画布和3D导演台工具:短剧分镜从素材整理到空间预演的完整链路
服务器·前端·数据库
专注仿真41 分钟前
TRAE Work 的实战
数据库·人工智能·分析
艾莉丝努力练剑44 分钟前
【AI大模型接入SDK】WebSocket & SSE 协议
网络·c++·websocket·网络协议·学习·面试
xieliyu.1 小时前
计算机网络‑IP 协议解析:核心特性总结
网络·笔记·网络协议·学习·tcp/ip·计算机网络
ACP广源盛139246256731 小时前
端侧 AI 硬件架构实践@ACP#中端边缘整机 PCIe 扩展与 IX7012 器件分析
大数据·数据库·人工智能·嵌入式硬件·开源·硬件架构
ACP广源盛139246256731 小时前
端侧 AI 硬件架构探讨@ACP#多外设高密度整机 PCIe 扩展与 IX7024 器件分析
大数据·数据库·人工智能·嵌入式硬件·开源·硬件架构