news 2026/4/25 3:14:27

ngx_epoll_add_event

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
ngx_epoll_add_event

1 定义

ngx_epoll_add_event 函数 定义在 ./nginx-1.24.0/src/event/modules/ngx_epoll_module.c
staticngx_int_tngx_epoll_add_event(ngx_event_t*ev,ngx_int_tevent,ngx_uint_tflags){intop;uint32_tevents,prev;ngx_event_t*e;ngx_connection_t*c;structepoll_eventee;c=ev->data;events=(uint32_t)event;if(event==NGX_READ_EVENT){e=c->write;prev=EPOLLOUT;#if(NGX_READ_EVENT!=EPOLLIN|EPOLLRDHUP)events=EPOLLIN|EPOLLRDHUP;#endif}else{e=c->read;prev=EPOLLIN|EPOLLRDHUP;#if(NGX_WRITE_EVENT!=EPOLLOUT)events=EPOLLOUT;#endif}if(e->active){op=EPOLL_CTL_MOD;events|=prev;}else{op=EPOLL_CTL_ADD;}#if(NGX_HAVE_EPOLLEXCLUSIVE&&NGX_HAVE_EPOLLRDHUP)if(flags&NGX_EXCLUSIVE_EVENT){events&=~EPOLLRDHUP;}#endifee.events=events|(uint32_t)flags;ee.data.ptr=(void*)((uintptr_t)c|ev->instance);ngx_log_debug3(NGX_LOG_DEBUG_EVENT,ev->log,0,"epoll add event: fd:%d op:%d ev:%08XD",c->fd,op,ee.events);if(epoll_ctl(ep,op,c->fd,&ee)==-1){ngx_log_error(NGX_LOG_ALERT,ev->log,ngx_errno,"epoll_ctl(%d, %d) failed",op,c->fd);returnNGX_ERROR;}ev->active=1;#if0ev->oneshot=(flags&NGX_ONESHOT_EVENT)?1:0;#endifreturnNGX_OK;}
ngx_epoll_add_event 函数的作用是: 负责将 NGINX 的读或写事件注册到 Linux epoll 实例中。 它根据事件类型构造对应的 epoll 监听掩码(读:`EPOLLIN|EPOLLRDHUP`,写:`EPOLLOUT`), 并自动判断应该向 epoll 添加新 fd 还是修改已有事件 (若同一连接的另一事件已活跃,则合并事件掩码进行修改,避免覆盖), 最后通过 `epoll_ctl` 完成操作,使连接的事件得以被异步监控。

2 详解

1 函数签名

staticngx_int_tngx_epoll_add_event(ngx_event_t*ev,ngx_int_tevent,ngx_uint_tflags)
返回值 成功返回 NGX_OK(值为 0), 失败返回 NGX_ERROR(值为 -1)
参数1 ngx_event_t *ev 指向 ngx_event_t 结构的指针 是本次要注册监听的事件
参数2 ngx_int_t event 事件类型 指明当前要注册的是读事件还是写事件
NGX_READ_EVENT:监听可读事件(对应 EPOLLIN | EPOLLRDHUP) NGX_WRITE_EVENT:监听可写事件(对应 EPOLLOUT)
参数3 ngx_uint_t flags 用来控制 epoll 行为的 控制标志 多个标志可通过按位或 | 组合传入

2 逻辑流程

1 局部变量 2 获取连接对象 3 事件配置 4 epoll_ctl 注册事件监听 5 返回成功

1 局部变量

{intop;uint32_tevents,prev;ngx_event_t*e;ngx_connection_t*c;structepoll_eventee;

2 获取连接对象
c=ev->data;
从事件对象中取出连接指针。 Nginx 中每个事件(ngx_event_t)的 data 字段都指向其所属的 ngx_connection_t 结构, 这样可以通过事件快速访问连接的所有信息

3 事件配置
events=(uint32_t)event;
初始化目标事件掩码。 将调用方传入的 Nginx 抽象事件常量强转为 32 位无符号整型。 作为初始值,后续会根据实际情况做 位运算

if(event==NGX_READ_EVENT){e=c->write;prev=EPOLLOUT;#if(NGX_READ_EVENT!=EPOLLIN|EPOLLRDHUP)events=EPOLLIN|EPOLLRDHUP;#endif}else{e=c->read;prev=EPOLLIN|EPOLLRDHUP;#if(NGX_WRITE_EVENT!=EPOLLOUT)events=EPOLLOUT;#endif}
#1-1 判断要操作的事件是否为读事件 若为读事件,则将对立事件指针 e 指向连接的写事件。 这是因为在一个连接上,读写事件是成对存在的, 当添加读事件时,需要关心写事件当前是否已经注册,以便做修改还是添加的决定 #1-2 如果写事件已经活跃,那么它在 epoll 中监听的标志通常是 EPOLLOUT(可写)。 这里预设 prev 为 EPOLLOUT,当执行 EPOLL_CTL_MOD 时, 将会通过 events |= prev 把写事件标志合并进去,从而不会丢失写事件的监听。 #1-3 这是条件编译: 如果宏 NGX_READ_EVENT 的值不等于 EPOLLIN|EPOLLRDHUP, 则把 events 设置为这两个标志的位或。 该条件编译通常为真,因为 Nginx 定义的 NGX_READ_EVENT 往往是一个简单的枚举值(如 0), 与真正的 epoll 位掩码不同。所以这里就是正确设置了读事件的监听集合。 EPOLLIN: 数据可读(包括正常数据和优先带数据)。 EPOLLRDHUP: 对端关闭连接或仅关闭写半部(半关闭), 这是 Linux 2.6.17 引入的标志,允许应用区分对端完全关闭和只关闭写。
#2-1 否则(即 event 为 NGX_WRITE_EVENT),处理写事件 对于写事件,对立事件是连接的读事件,因此 e 指向读事件 #2-2 如果读事件已经活跃,则它监听的标志通常是 EPOLLIN|EPOLLRDHUP。 这里设定 prev 为这个组合,以便在修改模式下保留读事件的监听。 #2-3 条件编译: 如果 NGX_WRITE_EVENT 宏的值不等于 EPOLLOUT, 则将 events 设为 EPOLLOUT(可写)。 这也是常见情况,因为写事件枚举值通常不与 EPOLLOUT 直接相等。

if(e->active){op=EPOLL_CTL_MOD;events|=prev;}else{op=EPOLL_CTL_ADD;}
#1 检查对立事件是否已经处于活跃状态(即是否已被 epoll 监控)。 e->active 标志会在事件成功通过 epoll_ctl 添加/修改后被置为 1。 若对立事件活跃,说明该连接对应的文件描述符(fd)已经在 epoll 中被监听 (epoll 是以 fd 为索引的,一个 fd 只能被添加一次,后续都需要修改)。 因此本次操作必须是 修改(EPOLL_CTL_MOD),不能是添加。 设置操作码为 EPOLL_CTL_MOD,表示修改已存在 fd 的监听事件 把对立事件的原有监听标志(prev)合并到当前要设置的 events 中。 因为 epoll 修改事件时,新的事件掩码会完全覆盖原来的掩码, 所以必须显式把另一方向的事件重新包含进去,否则另一方向的监听就会丢失。
#2 对立事件不活跃,说明该 fd 是第一次注册事件(或者之前的事件已被删除),因而执行添加操作 操作码设为 EPOLL_CTL_ADD,将 fd 首次加入 epoll 监听列表

#if(NGX_HAVE_EPOLLEXCLUSIVE&&NGX_HAVE_EPOLLRDHUP)if(flags&NGX_EXCLUSIVE_EVENT){events&=~EPOLLRDHUP;}#endif
这是一个条件编译块, 只有在系统支持 EPOLLEXCLUSIVE 并且也支持 EPOLLRDHUP 时才会编译。 EPOLLEXCLUSIVE 是 Linux 4.5+ 引入的标志, 用于多进程/多线程共享同一个监听 socket 时, 以独占方式唤醒等待队列中的进程,避免惊群效应(thundering herd)。 然而在某些内核实现上, EPOLLEXCLUSIVE 与 EPOLLRDHUP 一起使用可能会有问题(例如某些场景下破坏独占语义或造成未定义行为)。因此 Nginx 在这里主动清除 EPOLLRDHUP 标志。 如果调用传入了 NGX_EXCLUSIVE_EVENT 标志, 则执行 events &= ~EPOLLRDHUP,从事件掩码中去掉 EPOLLRDHUP 位。

ee.events=events|(uint32_t)flags;ee.data.ptr=(void*)((uintptr_t)c|ev->instance);ngx_log_debug3(NGX_LOG_DEBUG_EVENT,ev->log,0,"epoll add event: fd:%d op:%d ev:%08XD",c->fd,op,ee.events);
#1 构建最终传递给 epoll_ctl 的事件标志。 将前面得到的 events(例如 EPOLLIN|EPOLLOUT|EPOLLRDHUP) 与传入的 flags(可能包含 EPOLLET、EPOLLONESHOT、EPOLLEXCLUSIVE 等)做位或, 得到完整的 epoll 事件掩码。 注意 flags 的类型是 ngx_uint_t,这里显式转换为 uint32_t 以匹配 ee.events 的类型
#2 设置 epoll 事件携带的用户数据 ee.data.ptr 是 void *,通常用来存放事件相关的上下文指针。 Nginx 将连接指针 c 与事件的 instance 标志做位或,存入 ptr。 原因:c 是一个指针,其地址由于内存对齐(至少 4 或 8 字节对齐)而最低若干位始终为 0。 ev->instance 是一个 0 或 1 的整数,用来标识事件是否过期。 当 epoll 触发事件时,Nginx 会取出 ptr,通过隔离低位得到原来的连接指针((c = ptr & ~1)), 同时通过最低位判断 instance 是否与当前事件记录的 instance 一致, 从而识别并丢弃旧事件(例如连接已关闭但残留的事件)。 这样的位复用避免了额外内存开销,是一种精巧的设计
#3 输出调试日志

4 epoll_ctl 注册事件监听
if(epoll_ctl(ep,op,c->fd,&ee)==-1){ngx_log_error(NGX_LOG_ALERT,ev->log,ngx_errno,"epoll_ctl(%d, %d) failed",op,c->fd);returnNGX_ERROR;}
调用 Linux 系统调用 epoll_ctl,对全局 epoll 实例 ep(该模块内的全局变量)执行 op 操作, 修改 fd c->fd 的监听事件为 ee 所描述的内容。 返回值 -1 表示调用失败。

ev->active=1;#if0ev->oneshot=(flags&NGX_ONESHOT_EVENT)?1:0;#endif
将当前事件的 active 标志置为 1,表示该事件现在已被 epoll 成功监听。 这个标志会在将来移除事件或事件处理时被清除,同时上面的逻辑也依靠它来判断对立事件的状态

5 返回成功
returnNGX_OK;}
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/25 3:14:27

Go 的 maps.Copy:复制个 Map,居然也能又这么多坑

以前复制 Map 要写 for 循环,现在一行搞定。但别高兴太早,踩坑姿势不对,照样翻车~🤔 为什么需要 maps.Copy? 在 Go 1.21 之前,复制一个 Map 的"标准姿势"是这样的: // &am…

作者头像 李华
网站建设 2026/4/25 3:11:58

数据结构初涉----顺序表

有了我们之前共同学习的C做基础,我们本文开始学习数据结构,本文先从数据结构的基础-----顺序表开始介绍。顺序表的出现顺序表的基层原理其实就是数组,但是数组用来存放数据可以,遇到插入数据,删除数据这些操作时&#…

作者头像 李华
网站建设 2026/4/25 3:11:56

GBDT概率模型在空气污染预测中的应用实践

1. 项目背景与核心价值空气污染预测一直是环境科学和公共健康领域的重要课题。传统预测方法往往只能给出确定性结果,而概率预测模型则能提供更丰富的风险信息。这个项目构建的概率预测模型,能够量化未来出现污染天气的可能性,为决策者提供更科…

作者头像 李华
网站建设 2026/4/25 3:09:44

基于vDisk的高校实验室IDV云桌面安全管理方案

基于vDisk的高校实验室IDV云桌面安全管理方案本文是针对高校公共计算机实验室、AI实训机房,提供的可落地建设部署方案,以IDV架构结合vDisk虚拟磁盘统一管理为核心,解决实验室桌面基线混乱、数据安全难管控、合规审计缺失、AI教学环境部署慢的…

作者头像 李华
网站建设 2026/4/25 3:05:38

evolver部署教程:构建自动优化AI系统

在运行进化算法或自动优化类 AI 系统时,计算资源与运行稳定性会直接影响结果质量。尤其是在需要长时间迭代、批量实验或多轮计算的场景中,一些具备稳定资源与弹性能力的环境(如莱卡云服务器这类部署方式)通常更有利于实验持续推进…

作者头像 李华