1 定义
ngx_epoll_notify_init 函数 定义在 ./nginx-1.24.0/src/event/modules/ngx_epoll_module.c
staticngx_int_tngx_epoll_notify_init(ngx_log_t*log){structepoll_eventee;#if(NGX_HAVE_SYS_EVENTFD_H)notify_fd=eventfd(0,0);#elsenotify_fd=syscall(SYS_eventfd,0);#endifif(notify_fd==-1){ngx_log_error(NGX_LOG_EMERG,log,ngx_errno,"eventfd() failed");returnNGX_ERROR;}ngx_log_debug1(NGX_LOG_DEBUG_EVENT,log,0,"notify eventfd: %d",notify_fd);notify_event.handler=ngx_epoll_notify_handler;notify_event.log=log;notify_event.active=1;notify_conn.fd=notify_fd;notify_conn.read=¬ify_event;notify_conn.log=log;ee.events=EPOLLIN|EPOLLET;ee.data.ptr=¬ify_conn;if(epoll_ctl(ep,EPOLL_CTL_ADD,notify_fd,&ee)==-1){ngx_log_error(NGX_LOG_EMERG,log,ngx_errno,"epoll_ctl(EPOLL_CTL_ADD, eventfd) failed");if(close(notify_fd)==-1){ngx_log_error(NGX_LOG_ALERT,log,ngx_errno,"eventfd close() failed");}returnNGX_ERROR;}returnNGX_OK;}
ngx_epoll_notify_init 函数的作用是 初始化一个用于进程内部事件通知的 eventfd 对象, 并将其加入 epoll 监视集合, 以便工作进程或线程之间能够通过写入 eventfd 来唤醒 epoll 等待循环
2 详细
1 函数签名
staticngx_int_tngx_epoll_notify_init(ngx_log_t*log)
返回值 成功返回 NGX_OK, 失败返回 NGX_ERROR。
参数 Nginx 日志对象指针,用于记录错误或调试信息。
2 逻辑流程
1 局部变量 2 创建 eventfd 描述符 3 初始化全局变量 notify_event 和 notify_conn 4 配置 epoll 事件参数 5 将 notify_fd 加入 epoll 监听 6 返回成功
1 局部变量
{structepoll_eventee;
声明一个 epoll_event 结构体变量 ee, 用于后续向 epoll_ctl 传递需要监控的事件属性。
2 创建 eventfd 描述符
#if(NGX_HAVE_SYS_EVENTFD_H)notify_fd=eventfd(0,0);#elsenotify_fd=syscall(SYS_eventfd,0);#endifif(notify_fd==-1){ngx_log_error(NGX_LOG_EMERG,log,ngx_errno,"eventfd() failed");returnNGX_ERROR;}ngx_log_debug1(NGX_LOG_DEBUG_EVENT,log,0,"notify eventfd: %d",notify_fd);
#1 条件编译创建 eventfd 文件描述符 eventfd 是 Linux 特有的系统调用,用于创建一个事件通知文件描述符。 如果系统头文件 <sys/eventfd.h> 存在,则直接调用标准库函数 eventfd(0, 0)。 否则通过系统调用 syscall(SYS_eventfd, 0) 创建。 第一个参数 0 表示计数器初始值; 第二个参数 0 表示不使用额外标志(如 EFD_NONBLOCK)。 Nginx 依赖后续的 EPOLLET 边缘触发处理 模式来正确处理读写。 eventfd 是内核维护的一个 64 位无符号整数计数器。 向它写一个 8 字节整数,计数器累加; 读则返回当前值并清零。 它是 Linux 上最轻量的事件通知机制之一,仅占用一个文件描述符,无协议栈开销。
#2 若创建失败(返回 -1),记录紧急级别日志, 包含错误码 ngx_errno,并返回 NGX_ERROR。
#3 若编译时启用调试且日志级别包含 NGX_LOG_DEBUG_EVENT 时, 打印创建的 eventfd 文件描述符值。
3 初始化全局变量 notify_event 和 notify_conn
notify_event.handler=ngx_epoll_notify_handler;notify_event.log=log;notify_event.active=1;notify_conn.fd=notify_fd;notify_conn.read=¬ify_event;notify_conn.log=log;
#1 初始化事件结构 notify_event(全局/静态变量) handler:指向通知事件的处理函数 ngx_epoll_notify_handler,当 eventfd 可读时被调用。 该函数通常读取 eventfd 中的值以清除事件,并执行相应通知逻辑。 log:关联日志对象,用于事件处理过程中的日志输出。 active:标记该事件已激活(已添加到 epoll),避免重复添加。
#2 初始化连接结构 notify_conn(全局/静态变量) fd:文件描述符,即刚创建的 eventfd。 read:指向读事件的 ngx_event_t 结构体(notify_event), 因为 eventfd 是单向读写的,这里只关注读事件。 log:日志对象。
4 配置 epoll 事件参数
ee.events=EPOLLIN|EPOLLET;ee.data.ptr=¬ify_conn;
设置 epoll 事件属性 EPOLLIN:监听可读事件(当 eventfd 计数器大于 0 时可读)。 EPOLLET:边缘触发模式(Edge Triggered)。 边缘触发要求每次事件到来时只通知一次,必须一次性读完所有数据,否则会丢失后续就绪通知。 这里用边缘触发可减少不必要的系统调用开销。 data.ptr: 用户数据指针,指向 notify_conn, epoll_wait 返回时,该指针会原样传回,Nginx 借此找回对应的连接和事件结构。 这样 epoll 事件返回时可以直接获取对应的连接和事件结构。
5 将 notify_fd 加入 epoll 监听
if(epoll_ctl(ep,EPOLL_CTL_ADD,notify_fd,&ee)==-1){ngx_log_error(NGX_LOG_EMERG,log,ngx_errno,"epoll_ctl(EPOLL_CTL_ADD, eventfd) failed");if(close(notify_fd)==-1){ngx_log_error(NGX_LOG_ALERT,log,ngx_errno,"eventfd close() failed");}returnNGX_ERROR;}
将 eventfd 添加到 epoll 监听 ep:全局 epoll 文件描述符,由 epoll_create() 创建。 EPOLL_CTL_ADD:添加操作。 若失败返回 -1,进入错误处理分支。
6 返回成功
returnNGX_OK;}