1 定义ngx_epoll_process_events 函数 定义在 ./nginx-1.24.0/src/event/modules/ngx_epoll_module.cstaticngx_int_tngx_epoll_process_events(ngx_cycle_t*cycle,ngx_msec_ttimer,ngx_uint_tflags){intevents;uint32_trevents;ngx_int_tinstance,i;ngx_uint_tlevel;ngx_err_terr;ngx_event_t*rev,*wev;ngx_queue_t*queue;ngx_connection_t*c;/* NGX_TIMER_INFINITE INFTIM */ngx_log_debug1(NGX_LOG_DEBUG_EVENT,cycle-log,0,epoll timer: %M,timer);eventsepoll_wait(ep,event_list,(int)nevents,timer);err(events-1)?ngx_errno:0;if(flagsNGX_UPDATE_TIME||ngx_event_timer_alarm){ngx_time_update();}if(err){if(errNGX_EINTR){if(ngx_event_timer_alarm){ngx_event_timer_alarm0;returnNGX_OK;}levelNGX_LOG_INFO;}else{levelNGX_LOG_ALERT;}ngx_log_error(level,cycle-log,err,epoll_wait() failed);returnNGX_ERROR;}if(events0){if(timer!NGX_TIMER_INFINITE){returnNGX_OK;}ngx_log_error(NGX_LOG_ALERT,cycle-log,0,epoll_wait() returned no events without timeout);returnNGX_ERROR;}for(i0;ievents;i){cevent_list[i].data.ptr;instance(uintptr_t)c1;c(ngx_connection_t*)((uintptr_t)c(uintptr_t)~1);revc-read;if(c-fd-1||rev-instance!instance){/* * the stale event from a file descriptor * that was just closed in this iteration */ngx_log_debug1(NGX_LOG_DEBUG_EVENT,cycle-log,0,epoll: stale event %p,c);continue;}reventsevent_list[i].events;ngx_log_debug3(NGX_LOG_DEBUG_EVENT,cycle-log,0,epoll: fd:%d ev:%04XD d:%p,c-fd,revents,event_list[i].data.ptr);if(revents(EPOLLERR|EPOLLHUP)){ngx_log_debug2(NGX_LOG_DEBUG_EVENT,cycle-log,0,epoll_wait() error on fd:%d ev:%04XD,c-fd,revents);/* * if the error events were returned, add EPOLLIN and EPOLLOUT * to handle the events at least in one active handler */revents|EPOLLIN|EPOLLOUT;}#if0if(revents~(EPOLLIN|EPOLLOUT|EPOLLERR|EPOLLHUP)){ngx_log_error(NGX_LOG_ALERT,cycle-log,0,strange epoll_wait() events fd:%d ev:%04XD,c-fd,revents);}#endifif((reventsEPOLLIN)rev-active){#if(NGX_HAVE_EPOLLRDHUP)if(reventsEPOLLRDHUP){rev-pending_eof1;}#endifrev-ready1;rev-available-1;if(flagsNGX_POST_EVENTS){queuerev-accept?ngx_posted_accept_events:ngx_posted_events;ngx_post_event(rev,queue);}else{rev-handler(rev);}}wevc-write;if((reventsEPOLLOUT)wev-active){if(c-fd-1||wev-instance!instance){/* * the stale event from a file descriptor * that was just closed in this iteration */ngx_log_debug1(NGX_LOG_DEBUG_EVENT,cycle-log,0,epoll: stale event %p,c);continue;}wev-ready1;#if(NGX_THREADS)wev-complete1;#endifif(flagsNGX_POST_EVENTS){ngx_post_event(wev,ngx_posted_events);}else{wev-handler(wev);}}}returnNGX_OK;}ngx_epoll_process_events 函数的作用是 调用 epoll_wait 等待就绪事件 对每个事件进行检查 根据标志决定是立即执行读写回调函数 还是将事件放入待处理队列ngx_posted_events实现延迟分发。 同时负责更新缓存时间及处理定时器信号中断。2 详解1 函数签名staticngx_int_tngx_epoll_process_events(ngx_cycle_t*cycle,ngx_msec_ttimer,ngx_uint_tflags)返回值 NGX_OK函数正常完成事件已处理或队列化。 NGX_ERROR发生严重错误参数1 ngx_cycle_t *cycle 指向当前运行周期上下文环境 参数2 ngx_msec_t timer 传递给 epoll_wait 的超时时间 控制本次事件收集的最大等待时长 参数3 ngx_uint_t flags 位掩码标志 控制处理方式2 逻辑流程1 局部变量 2 调用 epoll_wait() 获取就绪事件 3 保存错误码 4 时间更新检查 5 错误处理 6 超时处理 7 遍历处理所有就绪事件 8 返回成功1 局部变量{intevents;uint32_trevents;ngx_int_tinstance,i;ngx_uint_tlevel;ngx_err_terr;ngx_event_t*rev,*wev;ngx_queue_t*queue;ngx_connection_t*c;/* NGX_TIMER_INFINITE INFTIM */ngx_log_debug1(NGX_LOG_DEBUG_EVENT,cycle-log,0,epoll timer: %M,timer);调试日志输出2 调用 epoll_wait() 获取就绪事件eventsepoll_wait(ep,event_list,(int)nevents,timer);调用 Linux epoll_wait在 epoll 实例 ep 上等待事件。 event_list 是存放就绪事件的数组nevents 是数组最大容量timer 是超时毫秒。 返回值 0就绪事件数量 0超时 -1出错errno 被设置。3 保存错误码err(events-1)?ngx_errno:0;保存错误码 如果 events -1立即将当前 errno 存入 err否则设为 0。 必须在任何可能改变 errno 的调用前完成。4 时间更新检查if(flagsNGX_UPDATE_TIME||ngx_event_timer_alarm){ngx_time_update();}时间缓存更新 如果调用者要求更新NGX_UPDATE_TIME 置位或者 定时器信号已触发ngx_event_timer_alarm 非0 ngx_event_timer_alarm 标记本次事件循环被定时器超时信号SIGALRM中断 调用 ngx_time_update() 将内核时间写入 Nginx 缓存变量 后续通过 ngx_time() 获取时无需系统调用。 这保证了事件处理过程中的时间精度。5 错误处理if(err){if(errNGX_EINTR){if(ngx_event_timer_alarm){ngx_event_timer_alarm0;returnNGX_OK;}levelNGX_LOG_INFO;}else{levelNGX_LOG_ALERT;}ngx_log_error(level,cycle-log,err,epoll_wait() failed);returnNGX_ERROR;}判断 epoll_wait 是否出错 进入错误处理分支。#1 判断是否为信号中断 EINTR 表示系统调用被信号打断通常是可以恢复的 定时器信号特判 如果 epoll_wait 被定时器信号SIGALRM唤醒 且机制已设置 ngx_event_timer_alarm 标志 则清除该标志并立即返回 NGX_OK 事件循环随后会重新调用此函数。 这样可避免将定时器信号当作普通错误。 普通信号中断的日志级别 若是其他信号如 SIGHUP 日志级别设为 INFO因为这是正常可恢复的中断。#2 非 EINTR 错误 其他系统错误如 EBADF、EFAULT视为严重问题日志级别设为 ALERT#3 记录错误并返回 使用前面确定的级别记录日志 然后返回 NGX_ERROR事件循环通常会终止或重启6 超时处理if(events0){if(timer!NGX_TIMER_INFINITE){returnNGX_OK;}ngx_log_error(NGX_LOG_ALERT,cycle-log,0,epoll_wait() returned no events without timeout);returnNGX_ERROR;}无就绪事件且无错误#1 判断是否超时 如果最初传入的 timer 不是无限等待 那么超时是预期的直接返回 NGX_OK事件循环继续#2 无限等待时的异常超时 理论上 epoll_wait 不会在无限超时下返回 0 除非有 bug如 epoll fd 已关闭视为严重错误 记录 ALERT 日志返回 NGX_ERROR。7 遍历处理所有就绪事件for(i0;ievents;i){cevent_list[i].data.ptr;遍历所有就绪事件 提取用户数据指针instance(uintptr_t)c1;c(ngx_connection_t*)((uintptr_t)c(uintptr_t)~1);分离 instance 标记与真实地址 取出最低位存为 instance。 将指针最低位清零得到真正的连接结构地址 crevc-read;获取连接上的读事件对象if(c-fd-1||rev-instance!instance){/* * the stale event from a file descriptor * that was just closed in this iteration */ngx_log_debug1(NGX_LOG_DEBUG_EVENT,cycle-log,0,epoll: stale event %p,c);continue;}陈旧事件检查 c-fd -1 表示该连接的文件描述符已经关闭。 rev-instance ! instance 表示读事件的实例号与事件中的不匹配说明该事件是旧的事件 连接已关闭并重新用新 fd 打开过。 记录调试日志然后用 continue 忽略该事件进入下一次循环reventsevent_list[i].events;获取当前事件的具体事件类型掩码ngx_log_debug3(NGX_LOG_DEBUG_EVENT,cycle-log,0,epoll: fd:%d ev:%04XD d:%p,c-fd,revents,event_list[i].data.ptr);调试日志if(revents(EPOLLERR|EPOLLHUP)){ngx_log_debug2(NGX_LOG_DEBUG_EVENT,cycle-log,0,epoll_wait() error on fd:%d ev:%04XD,c-fd,revents);/* * if the error events were returned, add EPOLLIN and EPOLLOUT * to handle the events at least in one active handler */revents|EPOLLIN|EPOLLOUT;}#1 检查是否有错误或挂起事件 EPOLLERRfd 发生错误 EPOLLHUPfd 被挂断如对端关闭连接。这些事件表示连接出现异常 #2 记录调试信息 #3 修改事件掩码 将 EPOLLIN 和 EPOLLOUT 合并到 revents后续读/写分支都会尝试处理 不在 epoll 分发层直接处理错误而是将错误“转嫁”给正常的读写事件处理流程 让每个连接自己的回调负责善后如关闭连接、释放资源#if0if(revents~(EPOLLIN|EPOLLOUT|EPOLLERR|EPOLLHUP)){ngx_log_error(NGX_LOG_ALERT,cycle-log,0,strange epoll_wait() events fd:%d ev:%04XD,c-fd,revents);}#endifif((reventsEPOLLIN)rev-active){#if(NGX_HAVE_EPOLLRDHUP)if(reventsEPOLLRDHUP){rev-pending_eof1;}#endifrev-ready1;rev-available-1;if(flagsNGX_POST_EVENTS){queuerev-accept?ngx_posted_accept_events:ngx_posted_events;ngx_post_event(rev,queue);}else{rev-handler(rev);}}#1 可读事件且读事件处于活跃状态 如果事件掩码包含 EPOLLIN 并且读事件对象 rev-active 为真表示在事件驱动中注册了读兴趣则处理读事件。 #2 处理半关闭对端关闭写端 如果系统支持 EPOLLRDHUPLinux 2.6.17且事件中设置该位 则将 rev-pending_eof 置 1表示即将到达文件结尾读完剩余数据后应关闭连接。 #3 标记读事件就绪 ready 1 表示事件已就绪随后调用 handler 可直接读取数据 available -1 表示尚未探测可读字节数#4 事件分发方式 如果 flags 包含 NGX_POST_EVENTS延迟处理模式 根据 rev-accept 判断是否为接受新连接的事件 是则放入优先队列 ngx_posted_accept_events 否则放入普通队列 ngx_posted_events。 调用 ngx_post_event 将事件加入队列稍后统一处理。 否则立即处理模式 直接调用读事件回调 rev-handler(rev)。wevc-write;if((reventsEPOLLOUT)wev-active){if(c-fd-1||wev-instance!instance){/* * the stale event from a file descriptor * that was just closed in this iteration */ngx_log_debug1(NGX_LOG_DEBUG_EVENT,cycle-log,0,epoll: stale event %p,c);continue;}wev-ready1;#if(NGX_THREADS)wev-complete1;#endifif(flagsNGX_POST_EVENTS){ngx_post_event(wev,ngx_posted_events);}else{wev-handler(wev);}}}#1 获取写事件对象#2 可写事件发生且写事件活跃#2-1 写事件的陈旧检查 同样判断 fd 是否已关闭或写事件的 instance 与事件中的不匹配防止处理过时的写事件#2-2 标记写事件就绪 如果启用了线程池NGX_THREADS 将 wev-complete 置 1 表示异步写操作已完成可通知上层。#2-3 写事件分发 延迟模式放入普通事件队列写事件不区分 accept。 立即模式直接调用写事件回调 wev-handler(wev)。8 返回成功returnNGX_OK;}