当前位置: 首页 > news >正文

ngx_close_listening_sockets

1 定义

ngx_close_listening_sockets 函数 定义在 ./nginx-1.24.0/src/core/ngx_connection.c
voidngx_close_listening_sockets(ngx_cycle_t*cycle){ngx_uint_ti;ngx_listening_t*ls;ngx_connection_t*c;if(ngx_event_flags&NGX_USE_IOCP_EVENT){return;}ngx_accept_mutex_held=0;ngx_use_accept_mutex=0;ls=cycle->listening.elts;for(i=0;i<cycle->listening.nelts;i++){c=ls[i].connection;if(c){if(c->read->active){if(ngx_event_flags&NGX_USE_EPOLL_EVENT){/* * it seems that Linux-2.6.x OpenVZ sends events * for closed shared listening sockets unless * the events was explicitly deleted */ngx_del_event(c->read,NGX_READ_EVENT,0);}else{ngx_del_event(c->read,NGX_READ_EVENT,NGX_CLOSE_EVENT);}}ngx_free_connection(c);c->fd=(ngx_socket_t)-1;}ngx_log_debug2(NGX_LOG_DEBUG_CORE,cycle->log,0,"close listening %V #%d ",&ls[i].addr_text,ls[i].fd);if(ngx_close_socket(ls[i].fd)==-1){ngx_log_error(NGX_LOG_EMERG,cycle->log,ngx_socket_errno,ngx_close_socket_n" %V failed",&ls[i].addr_text);}#if(NGX_HAVE_UNIX_DOMAIN)if(ls[i].sockaddr->sa_family==AF_UNIX&&ngx_process<=NGX_PROCESS_MASTER&&ngx_new_binary==0&&(!ls[i].inherited||ngx_getppid()!=ngx_parent)){u_char*name=ls[i].addr_text.data+sizeof("unix:")-1;if(ngx_delete_file(name)==NGX_FILE_ERROR){ngx_log_error(NGX_LOG_EMERG,cycle->log,ngx_socket_errno,ngx_delete_file_n" %s failed",name);}}#endifls[i].fd=(ngx_socket_t)-1;}cycle->listening.nelts=0;}
`ngx_close_listening_sockets` 函数用于 在进程退出、重启或配置 reload 时,安全关闭 `cycle` 中所有处于监听状态的套接字。 它遍历每个监听项,将其读事件从事件驱动模块中移除、 回收关联的连接结构、关闭底层套接字并妥善清理 Unix 域套接字文件, 最后将监听数量清零,完成监听资源的彻底释放。

2 详解

1 函数签名

voidngx_close_listening_sockets(ngx_cycle_t*cycle)
返回类型:void 函数不需要向调用者返回任何结果。 关闭监听套接字是一个资源清理过程,无论成功或失败, 函数都会尽量完成所有清理操作(仅通过日志记录错误), 调用者无需根据返回值采取不同行为。
参数 ngx_cycle_t *cycle 指向当前运行周期上下文

2 逻辑流程

1 局部变量 2 IOCP 事件模型检查 3 清理 accept 互斥锁状态 4 遍历监听套接字 5 重置监听总数

1 局部变量
{ngx_uint_ti;ngx_listening_t*ls;ngx_connection_t*c;

2 IOCP 事件模型检查
if(ngx_event_flags&NGX_USE_IOCP_EVENT){return;}
检查全局事件标志 ngx_event_flags 是否包含 NGX_USE_IOCP_EVENT(Windows IOCP 事件模型)。 如果当前使用 IOCP,则直接返回,不执行后续关闭逻辑。 原因在于 IOCP 对监听套接字的管理机制与其它事件模型(select/poll/epoll/kqueue)差异很大, 关闭操作需要通过 IOCP 专用函数处理,这里的通用路径不适用,强行走下去可能导致未定义行为或资源泄漏。 提前返回保证了代码的健壮性和平台兼容性。

3 清理 accept 互斥锁状态
ngx_accept_mutex_held=0;ngx_use_accept_mutex=0;
#1 将全局变量 ngx_accept_mutex_held 置 0。 该变量指示当前 worker 进程是否持有 accept 互斥锁。 既然即将关闭所有监听套接字,进程不再有能力 accept 新连接, 自然需要放弃锁状态,避免其它逻辑误以为仍持有锁。 #2 将全局变量 ngx_use_accept_mutex 置 0。 该变量表示是否启用 accept 互斥机制。 关闭监听时直接禁用互斥锁,可以防止在清理期间其它代码路径尝试获取或释放锁,确保状态一致。

4 遍历监听套接字
ls=cycle->listening.elts;for(i=0;i<cycle->listening.nelts;i++){c=ls[i].connection;if(c){if(c->read->active){if(ngx_event_flags&NGX_USE_EPOLL_EVENT){/* * it seems that Linux-2.6.x OpenVZ sends events * for closed shared listening sockets unless * the events was explicitly deleted */ngx_del_event(c->read,NGX_READ_EVENT,0);}else{ngx_del_event(c->read,NGX_READ_EVENT,NGX_CLOSE_EVENT);}}ngx_free_connection(c);c->fd=(ngx_socket_t)-1;}ngx_log_debug2(NGX_LOG_DEBUG_CORE,cycle->log,0,"close listening %V #%d ",&ls[i].addr_text,ls[i].fd);if(ngx_close_socket(ls[i].fd)==-1){ngx_log_error(NGX_LOG_EMERG,cycle->log,ngx_socket_errno,ngx_close_socket_n" %V failed",&ls[i].addr_text);}#if(NGX_HAVE_UNIX_DOMAIN)if(ls[i].sockaddr->sa_family==AF_UNIX&&ngx_process<=NGX_PROCESS_MASTER&&ngx_new_binary==0&&(!ls[i].inherited||ngx_getppid()!=ngx_parent)){u_char*name=ls[i].addr_text.data+sizeof("unix:")-1;if(ngx_delete_file(name)==NGX_FILE_ERROR){ngx_log_error(NGX_LOG_EMERG,cycle->log,ngx_socket_errno,ngx_delete_file_n" %s failed",name);}}#endifls[i].fd=(ngx_socket_t)-1;}
#1 获取 cycle 中监听数组的起始地址。 cycle->listening 是一个 ngx_array_t 类型的动态数组, 其 elts 成员指向实际数据存储区域的首地址,元素类型为 ngx_listening_t。 赋值给 ls,后续可通过 ls[i] 访问每个监听项。
#2 开始 for 循环,遍历所有监听项。 cycle->listening.nelts 是数组中有效元素的个数。 循环将对每个监听套接字依次执行清理操作。
#3 将当前监听项 ls[i] 的 connection 指针取出,赋给局部变量 c。 每个监听套接字都会分配一个 ngx_connection_t 对象,用于挂载读事件、记录套接字描述符等信息。 通过该连接可以访问事件状态,以便后续从事件驱动模块中删除
#4 检查连接对象 c 是否为非空。正常情况下一定存在,但这里是一种防御性编程,增加安全性。 若确实为 NULL,则跳过事件删除和连接释放步骤,直接关闭套接字。
#5 检查监听连接上的读事件是否处于活跃(active)状态。 active 标志表示该事件当前已被注册到事件驱动模块中。 如果事件已不活跃,则无需再从事件模块删除,可避免无效操作。
#6 判断当前事件模型是否为 epoll(Linux 特有)。 如果采用 epoll,需要特殊处理。 注释说明特殊处理的理由:在某些虚拟化环境(如 Linux 2.6.x 下的 OpenVZ)中, 即使监听套接字已经关闭,如果之前注册的 epoll 事件没有被显式删除(EPOLL_CTL_DEL), 内核仍可能继续发送事件,导致错误触发回调。 为了彻底避免这种问题,必须显式删除事件注册。 调用 ngx_del_event(c->read, NGX_READ_EVENT, 0) 删除 epoll 实例中的读事件。 若非 epoll 事件模型(如 select、poll、kqueue、/dev/poll 等), 则调用 ngx_del_event(c->read, NGX_READ_EVENT, NGX_CLOSE_EVENT)。
#7 释放连接对象 c。 调用 ngx_free_connection 将该连接结构归还给 Nginx 的连接池(cycle->free_connections), 使其可以被后续新连接复用。这样避免了内存泄漏,同时维持了连接池的高效利用。 显式将连接结构中的文件描述符字段 fd 设置为 -1(ngx_socket_t 类型的无效值)。 因为连接对象即将回池,如果不清除其旧描述符,将来被分配用于新连接且未立即设置有效 fd 时, 可能会意外引用一个已关闭的文件描述符,导致错误的读写或事件操作。 这是防御性初始化,确保状态干净。
#8 输出调试日志
#9 调用 ngx_close_socket(ls[i].fd) 真正关闭底层套接字描述符。 检查返回值是否为 -1,表示关闭失败。 如果关闭失败,记录一条 NGX_LOG_EMERG(紧急)级别的错误日志。
#9 检查当前监听项的地址族是否为 AF_UNIX(Unix 域套接字)。 只有 Unix 域套接字才会在文件系统中创建文件,TCP/UDP 不需要文件清理。 ngx_process <= NGX_PROCESS_MASTER 判断当前进程角色。 ngx_process 可以是 NGX_PROCESS_SINGLE(单进程模式)或 NGX_PROCESS_MASTER(多进程的 master 进程)。 只有这些管理型进程才有权限删除 Unix socket 文件; worker 进程不应删除,否则会影响 master 或其他 worker 再次使用。 ngx_new_binary == 0 判断当前是否不是“平滑升级”过程中新启动的二进制(新 master)。 平滑升级时,旧 master 传递监听套接字给新 master,新 master 启动后 ngx_new_binary 被设置为 1。 此时新 master 不应该删除 Unix socket 文件,因为旧 master 可能还在运行并使用它。 只有非升级场景才可安全删除。 !ls[i].inherited || ngx_getppid() != ngx_parent 是一个复杂条件。 ls[i].inherited 表示该监听套接字是否从父进程继承而来(通过环境变量传递)。 如果是继承的,且当前进程的父进程 ID (ngx_getppid()) 与存储的父进程 ID (ngx_parent) 不同, 说明原父进程已退出(如平滑升级时旧 master 终止),此时文件已无人使用,可以安全删除。 如果是非继承的套接字,即本地新创建的,则任何时候都可以删除。 所有条件都满足时,进入清理文件的代码块。
#10 计算 Unix socket 文件路径。 ls[i].addr_text 是类似 "unix:/path/to/socket" 的字符串, 通过跳过 "unix:" 前缀(长度为 sizeof("unix:") - 1,即 5), name 指针直接指向文件路径部分(/path/to/socket)。 这样就可以传递给文件删除函数。 调用 ngx_delete_file(name) 删除该文件。 若返回 NGX_FILE_ERROR(表示删除失败),则进入错误处理。 删除失败时输出紧急级别错误日志,包含错误原因和文件名。 同样,即使删除失败也不会中断清理流程,但需要通过日志告知管理员可能存在残留文件, 防止下次启动因文件已存在而绑定失败
#11 将当前监听项 ls[i] 的 fd 字段置为 -1,标记该监听项不再代表有效套接字。 即使数组元素仍存在(数组大小不变),无效的 fd 可防止其它代码意外操作已关闭的套接字。

5 重置监听总数
cycle->listening.nelts=0;}
将监听数组的有效元素个数清零。 nelts = 0 向 cycle 的其他使用者表明当前已没有任何监听套接字, 后续如果调用相关函数会看到数量为 0,从而避免数组越界访问或重复关闭。 这是最终的状态重置。
http://www.cnnetsun.cn/news/2117057.html

相关文章:

  • 终极指南:如何在Windows电脑上使用Ryujinx模拟器畅玩Switch游戏
  • Python AI模型推理慢?不是模型问题,是这6类Python运行时开销在 silently kill 你的吞吐——附火焰图诊断模板
  • just-bash:将Bash脚本编译为JavaScript的跨平台自动化解决方案
  • 从CTP到自研L2行情解析引擎:我们用237天重写Python金融协议栈,最终实现<15μs端到端解析延迟(仅限内部技术白皮书节选)
  • 极片涂布产线三菱PLC双通道通讯设计:以太网模块实现HMI+上位机同步交互
  • 保姆级教程:用ESP32和MicroPython驱动1.8寸ST7735S彩屏,5分钟显示你的第一张图片
  • 2026年主流英文会议纪要AI工具实测对比,半路杀出黑马,头部产品差距竟然这么大
  • 如何让30fps视频秒变60fps?Flowframes AI视频插帧完全指南
  • 【AI大模型】国内外主流大语言模型(LLM)全面对比解析
  • VBVR视频推理数据集构建与多模态标注技术解析
  • 5分钟掌握DOL-Lyra:Degrees of Lewdity汉化美化一站式解决方案
  • 专知智库发布《地理标志资产成熟度认证白皮书》:三维生态模型为区域特色经济打造“价值标尺”
  • 算法#四数之和
  • 《凌晨三点还在改AI率?好写作AI来当你的学术“消防员”》
  • 免费在电脑玩Switch游戏:Ryujinx模拟器5分钟快速入门终极指南
  • 别再死记硬背了!用Python手把手教你实现DAG拓扑排序(附LeetCode真题)
  • 如何在macOS上实现MKV等所有视频格式的快速预览:终极解决方案指南
  • 5分钟上手Ryujinx:在PC上完美运行Switch游戏的终极指南 [特殊字符]
  • 网络安全中的CC攻击是什么?CC攻击怎么防御?黑客技术零基础入门到精通,收藏这一篇就够了!
  • 基于规则引擎与加权随机的智能周菜单生成系统设计与实现
  • 如何高效管理XCOM 2模组:专业级配置解决方案终极指南
  • PDF文件体积优化深度解析:开源工具pdfsizeopt的架构设计与性能调优策略
  • 音乐标签编码终极解决方案:告别繁简乱码,构建统一音乐库
  • Windows Cleaner磁盘清理工具:3分钟解决C盘爆红难题
  • TED演讲数据集:从数据清洗到NLP分析与知识图谱构建
  • 解决Py-Scrcpy-Client安装过程中Cython编译错误的3种方法
  • Allwinner T527开发板解析:多屏显示与嵌入式应用
  • 从MySQL的B+树索引到Redis的跳表:一次搞懂数据库核心数据结构选型背后的权衡
  • 接口及其运用
  • HPH构造关键点解析