1 定义
ngx_epoll_add_connection 函数 定义在 ./nginx-1.24.0/src/event/modules/ngx_epoll_module.c
staticngx_int_tngx_epoll_add_connection(ngx_connection_t*c){structepoll_eventee;ee.events=EPOLLIN|EPOLLOUT|EPOLLET|EPOLLRDHUP;ee.data.ptr=(void*)((uintptr_t)c|c->read->instance);ngx_log_debug2(NGX_LOG_DEBUG_EVENT,c->log,0,"epoll add connection: fd:%d ev:%08XD",c->fd,ee.events);if(epoll_ctl(ep,EPOLL_CTL_ADD,c->fd,&ee)==-1){ngx_log_error(NGX_LOG_ALERT,c->log,ngx_errno,"epoll_ctl(EPOLL_CTL_ADD, %d) failed",c->fd);returnNGX_ERROR;}c->read->active=1;c->write->active=1;returnNGX_OK;}
ngx_epoll_add_connection 函数的作用是: 将一条 nginx 连接注册到 epoll 多路复用监听中, 同时关注读、写、对端半关闭及边缘触发事件, 并通过指针低位编码事件实例号来安全区分新旧事件; 注册成功后将连接的读、写事件标记为活跃状态,失败则记录错误并返回。
2 详解
1 函数签名
staticngx_int_tngx_epoll_add_connection(ngx_connection_t*c)
返回值 NGX_OK 表示操作成功。 NGX_ERROR 表示出现错误
参数 ngx_connection_t *c 本次要添加到 epoll 监听中的描述符所绑定的连接
2 逻辑流程
1 局部变量 2 设置 epoll_event 3 调用 epoll_ctl 4 设置 active 标志 5 返回成功
1 局部变量
{structepoll_eventee;
定义局部变量 ee,类型为 Linux epoll 专用的 struct epoll_event。 它将在栈上分配,用于向 epoll_ctl 传递要监听的事件标志和关联的用户数据。 声明为局部变量避免动态内存分配
2 设置 epoll_event
ee.events=EPOLLIN|EPOLLOUT|EPOLLET|EPOLLRDHUP;ee.data.ptr=(void*)((uintptr_t)c|c->read->instance);ngx_log_debug2(NGX_LOG_DEBUG_EVENT,c->log,0,"epoll add connection: fd:%d ev:%08XD",c->fd,ee.events);
#1 设置事件掩码,四个标志位通过按位或组合: EPOLLIN:套接字可读(有数据到达或新连接)。 EPOLLOUT:套接字可写(发送缓冲区有空闲)。 一次性同时加入读写监听,避免后续通过 EPOLL_CTL_MOD 频繁修改事件类型,减少系统调用次数。 EPOLLET:边缘触发模式。只在状态发生变化(不可读→可读、不可写→可写)的边沿产生一次事件通知。 这要求 nginx 每次必须循环读写直到返回 EAGAIN,否则会丢失后续事件, 但能大幅降低事件通知次数,提高高并发性能。 EPOLLRDHUP:对端半关闭或全关闭检测。 当对端调用 shutdown(SHUT_WR) 或正常关闭连接时,epoll 会立即通知, 而不必等到读事件返回 0 才后知后觉,这对 HTTP 长连接和流控尤为重要。
#2 将连接指针和事件实例号编码进 epoll_event 的用户数据字段,以便事件触发时回传。 epoll_event.data 是联合体,ptr 字段在 nginx 中被用于存放一个既包含对象指针又带有版本号的“复合值”。 c 是 ngx_connection_t *, 转换为足够容纳指针的整数类型 uintptr_t。 c->read->instance 用于标记事件版本。 当连接关闭后被重新分配给新连接时,该实例号会取反, 从而让同一个文件描述符上的新旧事件能被区分。 (uintptr_t) c | c->read->instance 利用连接指针至少 4 或 8 字节对齐、低位全 0 的特性, 将 instance 存放在指针的最低比特位。 由于使用了 |,若 instance 为 0 则无影响; 为 1 则置最低位为 1。 结果强制转换为 void * 赋给 ee.data.ptr。 事件循环后续通过 (ngx_connection_t *)(ptr & ~1) 取回连接指针,通过 ptr & 1 取出实例号, 与当前事件结构体的 instance 比对,若不一致则丢弃该过期事件。这是防止“幽灵事件”的关键设计。
#3 调试日志
3 调用 epoll_ctl
if(epoll_ctl(ep,EPOLL_CTL_ADD,c->fd,&ee)==-1){ngx_log_error(NGX_LOG_ALERT,c->log,ngx_errno,"epoll_ctl(EPOLL_CTL_ADD, %d) failed",c->fd);returnNGX_ERROR;}
调用系统函数 epoll_ctl 将连接的文件描述符添加到 epoll 实例中。 ep: 模块内全局变量,是模块初始化时通过 epoll_create 创建的 epoll 实例文件描述符,被所有连接共享。 它被声明为 static int ep,代表该文件局部全局单例。 EPOLL_CTL_ADD: 将 c->fd 注册进 epoll,开始监听指定事件。 &ee: 传入上面构造的事件结构体,一次性将所有配置生效。 若返回 -1 表示系统调用失败, 可能原因包括:fd 无效、权限不足、内存不足或 epoll 实例已满等。
4 设置 active 标志
c->read->active=1;c->write->active=1;
将连接关联的读事件和写事件的 active 标志置为 1
5 返回成功
returnNGX_OK;}