news 2026/6/13 17:53:09

main-初始化

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
main-初始化

1 逻辑流程

1 调试与错误映射初始化 1 ngx_debug_init() → 激活内部调试钩子与断言机制 2 ngx_strerror_init() → 绑定 OS errno 到可读描述字符串 (失败则退出) 2 命令行解析与版本信息 1 ngx_get_options() → 解析 argc/argv 提取 -c/-p/-g/-s 等参数 (失败则退出) 2 ngx_show_version 分支 → 打印编译版本信息,若非测试模式 (!ngx_test_config) 则直接 return 0 3 基础运行时组件初始化 1 ngx_max_sockets = -1 → 占位初始化 (TODO 标记,后续由 OS 探测覆盖) 2 ngx_time_init() → 建立时间缓存、时区基准与单调时钟 3 [条件编译] ngx_regex_init() → 初始化 PCRE 正则引擎上下文 4 ngx_pid / ngx_parent → 调用 getpid()/getppid() 获取当前与父进程 ID 5 ngx_log_init() → 创建日志对象、分配日志文件路径与句柄 (失败则退出) 4 [条件编译] SSL/TLS 加密库初始化 5 核心上下文初始化 1 ngx_memzero(&init_cycle) → 清零 ngx_cycle_t 结构体防历史脏数据污染 2 init_cycle.log = log → 将日志对象绑定至初始化上下文 3 ngx_cycle = &init_cycle → 设置全局指针指向 init_cycle 6 内存池创建 init_cycle.pool = ngx_create_pool(1024) → 分配 1KB 初始内存池 (失败则退出) 7 参数持久化 ngx_save_argv() → 持久化保存 CLI 参数至 cycle 上下文 8 基础路径设置 ngx_process_options() → 标准化配置路径、prefix 目录与 error_log 路径 9 OS 能力探测与底层依赖初始化 1 ngx_os_init() → 探测页大小/缓存行/最大 FD/并发限制等 OS 能力 ,设置进程标题环境,(失败则退出) 2 ngx_crc32_table_init() → 初始化 CRC32 哈希表 (严格依赖 os_init 获取的 cacheline_size) 3 ngx_slab_sizes_init() → 初始化 Slab 内存分配器尺寸分级 (严格依赖 os_init 获取的 pagesize) 4 ngx_add_inherited_sockets() → 接管父进程通过环境变量传递的监听 Socket (支持平滑热重启) 10 模块注册与预初始化 ngx_preinit_modules() → 将所有静态/动态模块指针注册至全局 ngx_modules 数组 (失败则退出) 11 创建完整的运行时周期对象 ngx_init_cycle 通过解析配置文件创建核心运行时环境(cycle)。 如果解析失败,则在测试模式下将错误输出到标准错误,并终止程序。

19 模块预初始化 20 创建完整的运行时周期对象 21 输出配置测试结果 22 处理控制信号 23 记录操作系统状态 24 全局运行周期指针的切换 25 进程模式调整 26 信号初始化 27 守护进程化(正常启动) 28 热升级时的守护进程标记 29 创建并写入 PID 文件 30 日志系统最终配置与清理 31 主循环启动

1 局部变量
{ngx_buf_t*b;ngx_log_t*log;ngx_uint_ti;ngx_cycle_t*cycle,init_cycle;ngx_conf_dump_t*cd;ngx_core_conf_t*ccf;

2 调试初始化
ngx_debug_init();
在Linux上:不需要特殊的初始化,所以定义为空。 在FreeBSD或其他BSD系统上:可能有实际内容,例如初始化某些内核级别的调试选项
  • ngx_debug_init

3 初始化错误码到错误字符串的映射机制
if(ngx_strerror_init()!=NGX_OK){return1;}
  • ngx_strerror_init
ngx_strerror_init() 负责建立系统错误码到错误描述字符串的映射。 在 Unix/Linux 系统中,当系统调用失败时,errno 会被设置为一个错误码(如 EACCES、ENOENT)。 nginx 需要能够将这些错误码转换成人类可读的错误信息,用于日志记录和错误输出。 在 Linux/Unix 环境下 实现:直接返回成功 原因:Unix 系统标准的 strerror_r 或 strerror 函数通常足够可靠且线程安全(或 Nginx 有自己的线程安全封装), 不需要额外的初始化步骤。 虽然什么都不做,但保留了调用接口,保证代码流程统一。 在 Windows 环境下 实现:可能包含实际的初始化逻辑。 Windows 的错误码机制(GetLastError)与 Unix 的 errno 不同

4 解析命令行参数
if(ngx_get_options(argc,argv)!=NGX_OK){return1;}
解析用户启动时 在命令行输入的参数 它决定了程序将执行哪种模式(测试、发信号、正常启动)
  • ngx_get_options

5 版本信息
if(ngx_show_version){ngx_show_version_info();if(!ngx_test_config){return0;}}
负责将 nginx 的版本信息输出到标准输出 ngx_show_version: 全局标志,由 ngx_get_options() 在解析命令行时设置。 当用户指定 -v(显示版本)或 -V(显示版本及编译参数)时,该标志被设为 1。 ngx_test_config: 全局标志,由 ngx_get_options() 在解析到 -t 或 -T 时设置为 1,表示仅测试配置文件而不实际运行服务。
场景 A: 仅查看版本 (nginx -v 或 nginx -V) ngx_show_version = 1 (真)。 ngx_test_config = 0 (假)。 进入外层 if,执行 ngx_show_version_info(),打印版本。 进入内层 if (!0),条件成立。 执行 return 0;。 结果:程序立即退出,速度极快,无资源消耗。
场景 B:仅测试配置 (nginx -t) ngx_show_version = 0 (假)。 ngx_test_config = 1 (真)。 外层 if 条件不成立,跳过整个代码块。 结果:程序继续向下执行,进行日志初始化、配置解析等,直到后面的 if (ngx_test_config) 块处理测试逻辑。
场景 C:查看版本 且 测试配置 (nginx -V -t) 这是一个组合命令的场景,也是内层 if 存在的意义。 ngx_show_version = 1 (真)(因为用了 -V)。 ngx_test_config = 1 (真)(因为用了 -t)。 进入外层 if,执行 ngx_show_version_info(),打印版本。 进入内层 if (!1),条件不成立。 不执行 return 0;。 结果:程序打印完版本后,继续向下执行,进行后续的初始化,以便完成配置测试的任务。

6 初始化最大 socket 文件描述符数量
/* TODO */ngx_max_sockets=-1;
ngx_max_sockets: 这是一个 Nginx 的全局变量。 它用于记录 Nginx 当前进程或整个系统允许打开的最大 Socket(套接字)数量 = -1:将其赋值为 -1。 -1 通常是一个哨兵值(Sentinel Value),表示“未初始化”、“未知”或“无效”。 它明确表示:“此时我们还不知道最大 Socket 数是多少,稍后再计算。”

7 时间系统初始化
ngx_time_init();
  • ngx_time_init

8 初始化正则表达式引擎
#if(NGX_PCRE)ngx_regex_init();#endif
在编译时启用了 PCRE 支持的前提下, 初始化正则表达式引擎。 #if (NGX_PCRE): 这是一个预处理器指令(Preprocessor Directive)。 它在编译阶段(而非运行阶段)起作用。 如果 NGX_PCRE 宏被定义为非零值(表示编译时开启了 PCRE 支持),则包含其中的代码; 否则,编译器会直接忽略这部分代码。 ngx_regex_init(): 调用 Nginx 的正则表达式初始化函数。 负责初始化 PCRE 库的内部状态、内存池、缓存等。 #endif: 结束条件编译块。

9 获取进程 id
ngx_pid=ngx_getpid();ngx_parent=ngx_getppid();
ngx_getpid():Nginx 封装的系统调用 getpid()。 ngx_getppid():Nginx 封装的系统调用 getppid()。 全局变量:ngx_pid 和 ngx_parent 是 Nginx 的全局变量,整个程序的任何地方都可以访问
为什么需要保存 PID/PPID ? Nginx 采用 Master-Worker 多进程模型, 进程身份管理至关重要 进程身份识别 Master 进程:需要知道自己是 Master,负责管理 Worker。 Worker 进程:需要知道自己是 Worker,负责处理请求。 信号处理需要进程的 PID (向 哪个进程发送信号)

10 日志系统初始化
log=ngx_log_init(ngx_prefix,ngx_error_log);if(log==NULL){return1;}

11 SSL/TLS 加密库初始化
/* STUB */#if(NGX_OPENSSL)ngx_ssl_init(log);#endif
它负责在编译时启用了 OpenSSL 支持的前提下, 初始化 SSL 环境,为后续的 HTTPS 连接提供加密基础

12 创建 init_cycle
/* * init_cycle->log is required for signal handlers and * ngx_process_options() */ngx_memzero(&init_cycle,sizeof(ngx_cycle_t));init_cycle.log=log;ngx_cycle=&init_cycle;init_cycle.pool=ngx_create_pool(1024,log);if(init_cycle.pool==NULL){return1;}
创建一个临时的 ngx_cycle_t 结构体(init_cycle), 用于承载早期初始化阶段所需的环境
#1 清空临时结构体 #2 设置日志对象 #3 将全局 ngx_cycle 指向临时结构 #4 创建内存池, 大小 1024 字节 如果内存池创建失败(通常是系统内存不足),main 函数返回 1,程序退出
init_cycle 与 cycle 的关系? cycle 是 Nginx 架构中最核心的数据结构之一,它是整个 Nginx 服务器的运行时上下文和生命线 本质:cycle 代表 Nginx 的一个完整"运行周期",包含了服务器运行时所需的所有核心数据 生命周期:从 ngx_init_cycle() 创建开始,直到 Nginx 退出时销毁 init_cycle 是临时周期对象,作为初始化阶段的临时载体, 用于在真正运行时的 cycle 对象创建之前,承载所有必需的基础资源和配置信息。 为什么需要两个 cycle 对象? init_cycle: 临时的、简化的周期对象,只包含初始化必需的最少信息 cycle: 最终的、完整的运行时周期对象,包含所有模块、配置、连接等完整信息 这种设计解决了鸡生蛋蛋生鸡的问题: 要创建完整的 cycle 需要很多基础资源,但这些资源本身又需要一个 cycle 来管理。 所以 init_cycle 是用来收集和管理 创建完整的 cycle 需要的资源的
  • ngx_create_pool

13 保存命令行参数
if(ngx_save_argv(&init_cycle,argc,argv)!=NGX_OK){return1;}
将命令行参数从栈上复制到临时 cycle 的内存池中, 使其持久化并纳入 nginx 的内存管理体系
  • ngx_save_argv

14 处理命令行选项
if(ngx_process_options(&init_cycle)!=NGX_OK){return1;}
应用之前解析出来的命令行参数
  • ngx_process_options

15 系统相关初始化
if(ngx_os_init(log)!=NGX_OK){return1;}
  • ngx_os_init

16 初始化 CRC32 查找表
/* * ngx_crc32_table_init() requires ngx_cacheline_size set in ngx_os_init() */if(ngx_crc32_table_init()!=NGX_OK){return1;}

17 slab 内存分配器初始化
/* * ngx_slab_sizes_init() requires ngx_pagesize set in ngx_os_init() */ngx_slab_sizes_init();
nginx 使用 slab 分配器 来管理共享内存 ngx_slab_sizes_init 计算并设置 Slab 分配器的内存块大小规则, 为后续创建共享内存池奠定基础。 为什么要初始化“尺寸”? Slab 分配器为了提高性能和减少碎片, 不支持任意大小的内存分配。 它将内存划分为不同大小的等级(Size Classes)。 根据操作系统页面大小计算 Slab 池的内存块尺寸等级, 为后续限流、状态统计等模块创建跨进程共享内存池提供准确的分配规则, 是 Nginx 实现多进程状态共享的底层基石。
  • ngx_slab_sizes_init

18 继承监听套接字
if(ngx_add_inherited_sockets(&init_cycle)!=NGX_OK){return1;}
在 Nginx 平滑升级场景中, 新进程会从旧进程继承监听 Socket,而不是重新创建。

19 模块预初始化
if(ngx_preinit_modules()!=NGX_OK){return1;}
预初始化所有 nginx 模块, 在配置解析之前注册所有模块 确保后续 ngx_init_cycle() 解析 配置文件 nginx.conf 时能够识别所有配置指令, 是 Nginx 从"代码"转变为"可配置服务"的关键桥梁—— 没有模块预初始化,配置文件中的任何指令都无法被识别和处理

20 创建完整的运行时周期对象
cycle=ngx_init_cycle(&init_cycle);if(cycle==NULL){if(ngx_test_config){ngx_log_stderr(0,"configuration file %s test failed",init_cycle.conf_file.data);}return1;}
基于临时的 init_cycle 创建完整的运行时周期 cycle 完成了从 "准备环境" 到 "可用服务" 的转变; 如果失败,根据是否为测试模式决定错误输出方式, 是 Nginx 能否成功启动的决定性步骤—— 在此之前都是准备工作, 在此之后 Nginx 才真正成为一个可处理请求的 Web 服务器。
  • ngx_init_cycle

21 输出配置测试结果
if(ngx_test_config){if(!ngx_quiet_mode){ngx_log_stderr(0,"configuration file %s test is successful",cycle->conf_file.data);}if(ngx_dump_config){cd=cycle->config_dump.elts;for(i=0;i<cycle->config_dump.nelts;i++){ngx_write_stdout("# configuration file ");(void)ngx_write_fd(ngx_stdout,cd[i].name.data,cd[i].name.len);ngx_write_stdout(":"NGX_LINEFEED);b=cd[i].buffer;(void)ngx_write_fd(ngx_stdout,b->pos,b->last-b->pos);ngx_write_stdout(NGX_LINEFEED);}}return0;}
#1 ngx_test_config 来源:由 ngx_get_options() 解析 -t 或 -T 参数时设置。 意义:区分"测试配置"和"启动服务"两种模式。 行为: 如果为真:输出测试结果后退出,不启动服务。 如果为假:跳过此块,继续启动流程。 #2 ngx_quiet_mode 静默模式检查 由 -q 参数设置。 若非静默模式,则通过 ngx_log_stderr(0, ...) 将成功信息输出到标准错误(stderr)。 ngx_log_stderr():直接输出到标准错误,不依赖日志系统。 -q 参数允许用户抑制成功信息,只保留错误输出。 #3 配置转储检查 ngx_dump_config:由 -T 参数设置。 cycle->config_dump:配置解析过程中收集的完整配置内容。 意义:输出解析后的配置,而非原始配置文件。 对于 config_dump 数组中的每一个条目: 输出 "# configuration file " 作为注释行。 输出文件名(如 http.conf 或 mime.types)。 输出一个冒号和换行符。 输出缓冲区的全部内容(从 b->pos 到 b->last)。 再输出一个换行符,分隔不同文件。 所有输出都通过 ngx_write_stdout()(内部调用 ngx_write_fd(ngx_stdout, ...))直接写入标准输出(stdout), 而不是标准错误。这样用户可以将转储结果重定向到文件(例如 nginx -T > combined.conf)。 #4 退出程序 在完成测试或转储后,程序返回 0,表示正常退出, 不会进入守护进程或启动 worker 进程。

22 处理控制信号
if(ngx_signal){returnngx_signal_process(cycle,ngx_signal);}
#1 处理命令行中通过 -s 选项指定的控制信号(如 stop、quit、reopen、reload), 将信号发送给已运行的 nginx 进程,然后立即退出当前程序。
#2 if (ngx_signal) 信号模式检查 检查是否处于信号控制模式 (-s) ngx_signal 信号类型变量 存储用户指定的信号类型 (reload, quit, stop, reopen) ngx_signal_process() 信号处理函数 向运行中的 Nginx 进程发送信号 cycle 参数 完整的运行周期(包含 PID 文件路径等信息) return 直接返回 发送信号后立即退出,不启动新服务

23 记录操作系统状态
ngx_os_status(cycle->log);
将当前操作系统的状态信息记录到日志中, 为后续的问题排查和性能分析提供基准参考

24 全局运行周期指针的切换
ngx_cycle=cycle;
将全局变量 ngx_cycle 从临时的 init_cycle 指向完整的运行时 cycle, 标志着 Nginx 正式进入运行状态。

25 进程模式调整
ccf=(ngx_core_conf_t*)ngx_get_conf(cycle->conf_ctx,ngx_core_module);if(ccf->master&&ngx_process==NGX_PROCESS_SINGLE){ngx_process=NGX_PROCESS_MASTER;}
#1 master: 是否启用 master 进程模式(1 表示多进程,0 表示单进程,默认 1)。 ngx_process 全局变量 可能取值: NGX_PROCESS_SINGLE:单进程模式(此时没有 master,只有一个进程处理所有请求)。 NGX_PROCESS_MASTER:多进程模式中的 master 进程。 NGX_PROCESS_WORKER:多进程模式中的 worker 进程。
#2 条件:ccf->master 为真(配置文件中 master_process on;,默认开启) 并且 ngx_process 当前是 NGX_PROCESS_SINGLE。 动作:将 ngx_process 修改为 NGX_PROCESS_MASTER。 这意味着:如果配置文件要求以 master 进程模式运行, 而当前进程尚未被标记为 master(仍处于初始单进程状态), 则将其切换为 master 模式。 后续代码会根据 ngx_process 的值决定启动流程。 为什么需要这个判断? 因为可能存在某些情况使得 ngx_process 在早期被显式设置为 NGX_PROCESS_SINGLE。 此时配置文件中的 master 指令为 on,应以配置为准,重新设为 master 模式。 这体现了配置文件的最高优先级: 命令行临时覆盖只在未指定配置时有效,一旦配置明确要求,则遵循配置。

26 信号初始化
#if!(NGX_WIN32)if(ngx_init_signals(cycle->log)!=NGX_OK){return1;}
为 nginx 关心的信号注册处理函数

27 守护进程化(正常启动)
if(!ngx_inherited&&ccf->daemon){if(ngx_daemon(cycle->log)!=NGX_OK){return1;}ngx_daemonized=1;}
ngx_inherited: 全局标志,表示当前进程是从旧 nginx 进程继承套接字启动的(热升级场景)。 ccf->daemon:核心模块配置中的 daemon 字段,由配置文件中的 daemon 指令决定(默认 on)。 逻辑: 条件:!ngx_inherited 且 ccf->daemon 为真 → 表示这是正常启动(非热升级), 且配置要求以守护进程方式运行。 调用 ngx_daemon(cycle->log): 该函数执行标准的守护进程化步骤: 成功:设置 ngx_daemonized = 1,标记当前进程已守护进程化。 失败:ngx_daemon() 返回 NGX_ERROR,主函数返回 1 退出。 意义:实现了 nginx 作为标准守护进程的行为,使其在后台运行, 不受终端关闭影响,同时保留了必要的日志输出。

28 热升级时的守护进程标记
if(ngx_inherited){ngx_daemonized=1;}#endif
当 nginx 通过 USR2 信号进行热升级时, 旧进程启动新进程并传递监听套接字。 新进程启动时,ngx_inherited 为 1,且已经是守护进程(因为旧进程是守护进程)。 逻辑: 此时不应再次调用 ngx_daemon()(否则会导致进程重复 fork 并丢失继承的套接字),但 需要设置 ngx_daemonized = 1,使后续代码知道当前进程已在守护进程环境中运行 ngx_daemonized 标志并非用于直接控制行为,而是用于日志、资源管理等方面的辅助判断。

29 创建并写入 PID 文件
if(ngx_create_pidfile(&ccf->pid,cycle->log)!=NGX_OK){return1;}
将当前进程的 PID(进程标识符)持久化到磁盘, 以便外部程序(如 nginx -s reload)能够找到并控制正在运行的 nginx 进程
  • ngx_create_pidfile

30 日志系统最终配置与清理
if(ngx_log_redirect_stderr(cycle)!=NGX_OK){return1;}if(log->file->fd!=ngx_stderr){if(ngx_close_file(log->file->fd)==NGX_FILE_ERROR){ngx_log_error(NGX_LOG_ALERT,cycle->log,ngx_errno,ngx_close_file_n" built-in log failed");}}ngx_use_stderr=0;
将标准错误(stderr)重定向到 nginx 的错误日志文件, 并清理不再需要的文件描述符,确保后台进程的日志输出准确且资源不浪费 ngx_use_stderr 是一个全局标志 初始值为 1(表示可以使用标准错误输出)。 将其设为 0,表示之后不应再直接向 stderr 写入信息,所有错误都应通过日志系统记录。

31 主循环启动
if(ngx_process==NGX_PROCESS_SINGLE){ngx_single_process_cycle(cycle);}else{ngx_master_process_cycle(cycle);}
根据进程模式选择调用单进程循环或主进程循环, 标志着 Nginx 从"初始化阶段"正式进入"运行阶段"。 进入无限事件循环开始处理请求, 从"初始化状态"正式转变为"服务状态"—— 在此之前 Nginx 是"准备中的程序", 在此之后 Nginx 是"运行中的服务器",这是整个 main() 函数的最终使命。

return0;}
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/13 17:50:49

如何用Java构建i茅台自动预约系统:从手动抢购到全自动化

如何用Java构建i茅台自动预约系统&#xff1a;从手动抢购到全自动化 【免费下载链接】campus-imaotai i茅台app自动预约&#xff0c;每日自动预约&#xff0c;支持docker一键部署&#xff08;本项目不提供成品&#xff0c;使用的是已淘汰的算法&#xff09; 项目地址: https:…

作者头像 李华
网站建设 2026/6/13 17:46:11

Duplicity:终极免费的《缺氧》游戏存档编辑器完整指南

Duplicity&#xff1a;终极免费的《缺氧》游戏存档编辑器完整指南 【免费下载链接】oni-duplicity A web-hosted, locally-running save editor for Oxygen Not Included. 项目地址: https://gitcode.com/gh_mirrors/on/oni-duplicity 还在为《缺氧》游戏中那些难以平衡…

作者头像 李华
网站建设 2026/6/13 17:42:54

从命令行到可视化界面:TegraRcmGUI如何重塑Switch注入体验

从命令行到可视化界面&#xff1a;TegraRcmGUI如何重塑Switch注入体验 【免费下载链接】TegraRcmGUI C GUI for TegraRcmSmash (Fuse Gele exploit for Nintendo Switch) 项目地址: https://gitcode.com/gh_mirrors/te/TegraRcmGUI 在Nintendo Switch自制系统社区中&…

作者头像 李华
网站建设 2026/6/13 17:41:58

深入解析NXP LS1046A SEC引擎CCB寄存器:硬件加密加速实战指南

1. 项目概述与SEC引擎核心架构解析在嵌入式网络处理器领域&#xff0c;尤其是面对5G网关、SD-WAN设备、工业防火墙等高吞吐量、低延迟的应用场景&#xff0c;纯软件实现的数据加解密和完整性校验往往会成为系统性能的瓶颈。NXP的QorIQ LS1046A处理器集成的安全引擎&#xff08;…

作者头像 李华
网站建设 2026/6/13 17:39:51

终极指南:5步免费获取Grammarly Premium高级版完整教程

终极指南&#xff1a;5步免费获取Grammarly Premium高级版完整教程 【免费下载链接】autosearch-grammarly-premium-cookie 免费白嫖使用Grammarly Premium高级版 项目地址: https://gitcode.com/gh_mirrors/au/autosearch-grammarly-premium-cookie 想要免费使用Gramma…

作者头像 李华
网站建设 2026/6/13 17:34:04

模块化一体化安全平台 Avast One 技术架构与防护效能研究

摘要&#xff1a;传统终端安全产品普遍存在功能固化、模块耦合度高、安全防护与隐私保护、设备性能优化割裂等问题&#xff0c;难以应对当下融合钓鱼攻击、恶意代码、数据泄露、网络窃听的复合型网络威胁。Avast One 作为新一代模块化一体化数字安全平台&#xff0c;重构了终端…

作者头像 李华