ngx_epoll_test_rdhup
1 定义ngx_epoll_test_rdhup 函数 定义在 ./nginx-1.24.0/src/event/modules/ngx_epoll_module.cstaticvoidngx_epoll_test_rdhup(ngx_cycle_t*cycle){ints[2],events;structepoll_eventee;if(socketpair(AF_UNIX,SOCK_STREAM,0,s)-1){ngx_log_error(NGX_LOG_ALERT,cycle-log,ngx_errno,socketpair() failed);return;}ee.eventsEPOLLET|EPOLLIN|EPOLLRDHUP;if(epoll_ctl(ep,EPOLL_CTL_ADD,s[0],ee)-1){ngx_log_error(NGX_LOG_ALERT,cycle-log,ngx_errno,epoll_ctl() failed);gotofailed;}if(close(s[1])-1){ngx_log_error(NGX_LOG_ALERT,cycle-log,ngx_errno,close() failed);s[1]-1;gotofailed;}s[1]-1;eventsepoll_wait(ep,ee,1,5000);if(events-1){ngx_log_error(NGX_LOG_ALERT,cycle-log,ngx_errno,epoll_wait() failed);gotofailed;}if(events){ngx_use_epoll_rdhupee.eventsEPOLLRDHUP;}else{ngx_log_error(NGX_LOG_ALERT,cycle-log,NGX_ETIMEDOUT,epoll_wait() timed out);}ngx_log_debug1(NGX_LOG_DEBUG_EVENT,cycle-log,0,testing the EPOLLRDHUP flag: %s,ngx_use_epoll_rdhup?success:fail);failed:if(s[1]!-1close(s[1])-1){ngx_log_error(NGX_LOG_ALERT,cycle-log,ngx_errno,close() failed);}if(close(s[0])-1){ngx_log_error(NGX_LOG_ALERT,cycle-log,ngx_errno,close() failed);}}ngx_epoll_test_rdhup 函数的作用是 用于运行时检测当前 Linux 内核是否支持 EPOLLRDHUP 事件标志的辅助函数。2 详解1 函数签名staticvoidngx_epoll_test_rdhup(ngx_cycle_t*cycle)函数无返回值 参数 ngx_cycle_t *cycle 指向当前运行周期上下文2 逻辑流程1 局部变量 2 创建 socket 3 测试 EPOLLRDHUP 4 清理资源1 局部变量{ints[2],events;structepoll_eventee;int s[2] 用于存放 socketpair 创建的一对已连接 Unix 域 socket 文件描述符。 int events 保存 epoll_wait 返回的就绪事件数量。 struct epoll_event ee epoll 事件结构体用于注册和接收事件。2 创建 socketif(socketpair(AF_UNIX,SOCK_STREAM,0,s)-1){ngx_log_error(NGX_LOG_ALERT,cycle-log,ngx_errno,socketpair() failed);return;}socketpair(AF_UNIX, SOCK_STREAM, 0, s) 创建一对相互连接的 Unix 域流式 socket。s[0] 和 s[1] 是已连接的全双工通道。 若失败则记录错误并直接返回不再进行后续检测此时 ngx_use_epoll_rdhup 保持默认值 0。 因探测失败不影响 Nginx 启动保守策略是放弃探测。3 测试 EPOLLRDHUPee.eventsEPOLLET|EPOLLIN|EPOLLRDHUP;设置要监听的事件 EPOLLET边缘触发模式高效模式需配合非阻塞 IO。 EPOLLIN可读事件。 EPOLLRDHUP待检测的标志位表示对端关闭了写端读半关闭。if(epoll_ctl(ep,EPOLL_CTL_ADD,s[0],ee)-1){ngx_log_error(NGX_LOG_ALERT,cycle-log,ngx_errno,epoll_ctl() failed);gotofailed;}epoll_ctl(ep, EPOLL_CTL_ADD, s[0], ee) 将 s[0] 加入 epoll 实例 ep全局变量已在之前创建。 若添加失败则跳转到清理标签 failed释放资源并返回。if(close(s[1])-1){ngx_log_error(NGX_LOG_ALERT,cycle-log,ngx_errno,close() failed);s[1]-1;gotofailed;}主动关闭 socket 对的另一端。这会触发 TCP 层面的 FIN 发送对端 s[0] 将收到读关闭事件。 若关闭失败将 s[1] 置为 -1 防止二次关闭并跳转清理。s[1]-1;标记 s[1] 已关闭避免在后续清理代码中重复关闭。eventsepoll_wait(ep,ee,1,5000);epoll_wait等待 epoll 事件发生超时时间设为 5000 毫秒5 秒。 正常情况下由于 s[1] 已被关闭s[0] 应立即产生 EPOLLRDHUP 事件若内核支持。if(events-1){ngx_log_error(NGX_LOG_ALERT,cycle-log,ngx_errno,epoll_wait() failed);gotofailed;}如果 epoll_wait 调用本身出错如信号中断记录错误并跳转清理。if(events){ngx_use_epoll_rdhupee.eventsEPOLLRDHUP;}else{ngx_log_error(NGX_LOG_ALERT,cycle-log,NGX_ETIMEDOUT,epoll_wait() timed out);}ngx_log_debug1(NGX_LOG_DEBUG_EVENT,cycle-log,0,testing the EPOLLRDHUP flag: %s,ngx_use_epoll_rdhup?success:fail);#1 若 events 0表示有事件返回。 检查返回的事件结构体 ee.events 中是否包含 EPOLLRDHUP 标志 并将结果赋给全局变量 ngx_use_epoll_rdhup非零表示支持0 表示不支持。 #2 超时处理若 5 秒内未等到任何事件events 0 通常意味着内核不支持 EPOLLRDHUP 记录超时告警且 ngx_use_epoll_rdhup 保持默认值 0。 #3 打印调试日志4 清理资源failed:if(s[1]!-1close(s[1])-1){ngx_log_error(NGX_LOG_ALERT,cycle-log,ngx_errno,close() failed);}if(close(s[0])-1){ngx_log_error(NGX_LOG_ALERT,cycle-log,ngx_errno,close() failed);}}统一清理出口 无论正常执行还是中途 goto failed都会安全关闭两个 socket。 s[1] ! -1 检查确保不会重复关闭已关闭的描述符。 保证函数退出时不泄露文件描述