epoll_create1(epoll_cloexec)用于创建带close-on-exec标志的epoll实例,确保fork/exec后子进程自动关闭epoll fd,防止fd泄漏、事件误触发、ebadf错误或静默丢事件,尤其在daemon化或prefork模型中必不可少。

epoll_create1 为什么必须用 EPOLL_CLOEXEC 标志
不加 EPOLL_CLOEXEC 会导致子进程继承 epoll fd,后续 fork 后父子进程共用同一 epoll 实例,事件可能在错误线程中触发,甚至引发 EBADF 或静默丢事件。尤其在 daemon 化或 prefork 模型中极易出问题。
- 始终用
epoll_create1(EPOLL_CLOEXEC),别用已废弃的epoll_create - 如果需兼容旧内核(epoll_create(size) +
fcntl(epoll_fd, F_SETFD, FD_CLOEXEC) - 检查返回值:小于 0 时立即报错,不要忽略
-1
epoll_ctl 的 EPOLLONESHOT 真正作用和误用场景
EPOLLONESHOT 不是“只触发一次回调”,而是“触发后自动禁用该 fd 上所有事件,直到你显式调用 epoll_ctl(..., EPOLL_CTL_MOD, ...) 重置”。它解决的是多线程并发处理同一 fd 时的竞争问题,但代价是每次处理完必须手动恢复监听 —— 忘记这步就等于 fd 彻底失联。
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
- 仅在多线程共享同一个 epoll 实例、且对同一 fd 做读写混用时启用
- 单线程 event loop 中基本不需要,反而增加冗余
EPOLL_CTL_MOD调用开销 - 使用时务必配对:收到事件 → 处理 → 调用
epoll_ctl(EPOLL_CTL_MOD)恢复监听,否则后续数据永远收不到
回调函数如何安全捕获 this 指针而不引发悬垂引用
直接把成员函数地址传给 epoll 回调(比如用 std::bind 或 lambda 捕获 this)极危险:对象可能在事件触发前已被析构,回调执行时访问野指针。C++ 没有运行时对象生命周期检查,崩溃往往延迟出现、难以复现。
- 用
std::shared_ptr<yourclass></yourclass>管理 event handler 对象,把shared_from_this()传入回调上下文 - epoll data.ptr 字段存
void*,实际存std::shared_ptr<yourclass>*</yourclass>(注意是指针,避免拷贝 shared_ptr) - 回调入口第一行做空指针检查 +
lock()(若用 weak_ptr)或直接解引用(若确保生命周期由 event loop 持有) - 禁止在回调里直接 delete 或 reset 持有该对象的 shared_ptr,应交由 event loop 统一管理销毁时机
边缘触发(ET)模式下 read 循环必须带 EAGAIN/EWOULDBLOCK 退出条件
ET 模式下,epoll 只在 fd 状态从“不可读”变为“可读”时通知一次。如果你只 read 一次就返回,剩余数据会滞留在内核 socket buffer,但 epoll 再也不会提醒你 —— 直到下次新数据到达才再次触发。结果就是半包、粘包、连接假死。
- ET 模式下
read()必须循环,直到返回-1且errno == EAGAIN || errno == EWOULDBLOCK - 不能只判断
ret == 0(对端关闭)就跳出,要先确保本次缓冲区已清空 - 搭配非阻塞 socket(
fcntl(fd, F_SETFL, O_NONBLOCK)),否则循环会阻塞住整个 event loop - 注意
recv()和read()在 TCP 中行为一致,但 UDP 建议用recv()避免消息截断
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!










