Nginx 源码阅读笔记4 启动流程
来源:互联网 发布:ubuntu下安装mysql 编辑:程序博客网 时间:2024/05/17 00:15
总体流程
以下代码删掉了部分内容,也就是启动时指定-v -V -t -T
选项时用于显示版本ngx_show_version
,测试配置ngx_test_config
,显示配置ngx_dump_config
的相关代码,因为这里主要分析正常启动的流程,所以不关心这些参数
int ngx_cdeclmain(int argc, char *const *argv){ ngx_buf_t *b; ngx_log_t *log; ngx_uint_t i; ngx_cycle_t *cycle, init_cycle; ngx_core_conf_t *ccf; ngx_debug_init(); // 位于 os/unix/ngx_linux_config.h if (ngx_strerror_init() != NGX_OK) { // 位于 os/unix/ngx_errno.c return 1; } if (ngx_get_options(argc, argv) != NGX_OK) { return 1; } /* TODO */ ngx_max_sockets = -1; ngx_time_init(); // 位于 core/ngx_times.c#if (NGX_PCRE) ngx_regex_init();#endif ngx_pid = ngx_getpid(); log = ngx_log_init(ngx_prefix); // 位于 core/ngx_log.c if (log == NULL) { return 1; } /* STUB */#if (NGX_OPENSSL) ngx_ssl_init(log);#endif 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) { return 1; } if (ngx_save_argv(&init_cycle, argc, argv) != NGX_OK) { return 1; } if (ngx_process_options(&init_cycle) != NGX_OK) { return 1; } if (ngx_os_init(log) != NGX_OK) { // 位于 os/unix/ngx_posix_init.c return 1; } if (ngx_crc32_table_init() != NGX_OK) { return 1; } if (ngx_add_inherited_sockets(&init_cycle) != NGX_OK) { return 1; } if (ngx_preinit_modules() != NGX_OK) { return 1; } cycle = ngx_init_cycle(&init_cycle); if (cycle == NULL) { return 1; } if (ngx_signal) { return ngx_signal_process(cycle, ngx_signal); } ngx_os_status(cycle->log); ngx_cycle = cycle; 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; }#if !(NGX_WIN32) if (ngx_init_signals(cycle->log) != NGX_OK) { // 位于 os/unix/ngx_process.c return 1; } if (!ngx_inherited && ccf->daemon) { if (ngx_daemon(cycle->log) != NGX_OK) { // 位于 os/unix/ngx_daemon.c return 1; } ngx_daemonized = 1; } if (ngx_inherited) { ngx_daemonized = 1; }#endif if (ngx_create_pidfile(&ccf->pid, cycle->log) != NGX_OK) { return 1; } if (ngx_log_redirect_stderr(cycle) != NGX_OK) { // 位于 core/ngx_log.c return 1; } 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; if (ngx_process == NGX_PROCESS_SINGLE) { ngx_single_process_cycle(cycle); } else { ngx_master_process_cycle(cycle); // 位于 os/unix/ngx_process_cycle.c } return 0;}
- 调用
ngx_debug_init
,因为这个函数是个宏定义,而且为空,所以不知道什么作用,忽略吧 - 调用
ngx_strerror_init
,这个函数比较简单,主要就是将系统中 errno 的描述字符串,存储到一个ngx_str_t
数组中,这么做的原因是strerror
和strerror_r
不是信号安全的 - 调用
ngx_get_options
,根据argv
中的参数,设置相关的变量,除了一开始说的测试相关的,还有若含有了p
选项则设置ngx_prefix
表示路径前缀,含有c
选项则设置ngx_conf_file
表示配置文件相对路径,含有g
选项设置ngx_conf_params
表示额外配置项(语法与配置文件相同),这里的重点应该是s
选项,用于向master
进程发送信号,后面跟着stop quit reopen reload
的其中之一,具体后面会讲 - 调用
ngx_time_init
,设置了描述时间的字符串长度,并调用ngx_time_update
更新初始时间 - 调用
ngx_regex_init
,看起来是初始化了正则模块,这个模块我没用过,详情不太了解 - 调用
ngx_getpid
,设置表示进程 pid 的变量ngx_pid
- 调用
ngx_log_init
,初始化了log
对象用于输出错误日志(仅用于初始化),函数大概看了一下,就是根据前缀ngx_prefix
打开错误日志文件,默认路径是NGX_ERROR_LOG_PATH
,这个宏定义由configure
脚本生成,位于objs/ngx_auto_config.h
,如果不存在这个宏定义,则输出到stderr
- 调用
ngx_ssl_init
,看起来是初始化openssl
这个库 - 设置
init_cycle
变量,这个变量的类型是ngx_cycle_t
,这个类型之后会介绍,现在只知道设置了之前初始化的log
用于输出错误,创建了内存池pool
用于分配内存 - 调用
ngx_save_argv
,主要是将argv
拷贝到了ngx_argv
,原因应该是之后需要修改argv[0]
的值以修改进程名称(可能会覆盖argv[1]的值),所以需要备份参数到ngx_argv
- 调用
ngx_process_options
,设置init_cycle
中的几个参数,分别为conf_file
配置文件绝对路径(ngx_conf_file
指定的是相对路径),conf_param
额外参数(与ngx_conf_params
相同),conf_prefix
配置文件前缀,prefix
路径前缀,一般情况下,最后两个变量值相同,但是当配置头文件中的NGX_PREFIX
和NGX_CONF_PREFIX
存在且没使用c
选项指定ngx_prefix
时,两者可能不同 - 调用
ngx_os_init
,初始化系统相关信息,例如例如 cpu 数量,缓存行大小,页大小,文件描述符最大数量 - 调用
ngx_crc32_table_init
,如果设置了缓存行大小,则将crc32
算法需要用到的ngx_crc32_table16
数组的首地址对齐到缓存行大小 - 调用
ngx_add_inherited_sockets
,继承文件描述符,关于这个函数的细节写在后面 - 调用
ngx_preinit_modules
,设置ngx_modules
数组中各个模块的index
和name
- 调用
ngx_init_cycle
,这个函数涉及的东西很多,打算之后再说,目前知道解析了配置文件就行了 - 根据
ngx_signal
的值判断是否以选项s
启动,是则调用ngx_signal_process
发送信号,这个函数从指定文件(默认是nginx.pid
,这个文件会在后面创建)中读取master
进程的 pid,然后向其发送指定信号,如果以这个选项运行则到这里就结束了 - 调用
ngx_os_status
,将系统信息写入日志文件 - 设置
ngx_cycle
并根据解析完配置文件得到的信息判断是否以master
形式启动 - 调用
ngx_init_signals
,初始化所有信号处理函数,具体有哪些信号之后再说 - 如果不是平滑升级的情况,并且需要以 daemon 的形式启动,则调用
ngx_daemon
将进程变为守护进程(具体细节参考可 APUE 这类书籍)并将ngx_daemonized
设置为 1,如果是平滑升级的情况则直接将ngx_daemonized
设置为 1(这里不懂为什么) - 调用
ngx_create_pidfile
,创建记录进程 pid 的文件,用于向本进程发送信号 - 调用
ngx_log_redirect_stderr
,如果不使用stderr
则将其重定向到cycle->log
中指定的日志文件 - 如果
log
(由ngx_log_init
函数返回的临时log
,与cycle->log
不同,仅用于初始化)的文件描述符不等于ngx_stderr
则关闭它 - 将
ngx_use_stderr
设置为 0,这个变量感觉有点奇怪,初始化时为 1,到这里设置为 0,从代码里可以看出,有一个作用是判断是否需要在ngx_init_cycle
里调用ngx_log_redirect_stderr
,启动时为 1 所以不调用,后面每次 reload 时都会调用,还有一个作用就是ngx_log_error_core
函数中,在初始化时将错误输出到控制台,总之不是很懂 - 根据
ngx_process
判断是以master
形式还是single
形式启动
继承文件描述符
static ngx_int_tngx_add_inherited_sockets(ngx_cycle_t *cycle){ u_char *p, *v, *inherited; ngx_int_t s; ngx_listening_t *ls; inherited = (u_char *) getenv(NGINX_VAR); if (inherited == NULL) { return NGX_OK; } if (ngx_array_init(&cycle->listening, cycle->pool, 10, sizeof(ngx_listening_t)) != NGX_OK) { return NGX_ERROR; } for (p = inherited, v = p; *p; p++) { if (*p == ':' || *p == ';') { s = ngx_atoi(v, p - v); if (s == NGX_ERROR) { break; } v = p + 1; ls = ngx_array_push(&cycle->listening); if (ls == NULL) { return NGX_ERROR; } ngx_memzero(ls, sizeof(ngx_listening_t)); ls->fd = (ngx_socket_t) s; } } if (v != p) { ngx_log_error(NGX_LOG_EMERG, cycle->log, 0, "invalid socket number \"%s\" in " NGINX_VAR " environment variable, ignoring", v); } ngx_inherited = 1; return ngx_set_inherited_sockets(cycle);}
这个函数整体比较好看,一开始从环境变量NGINX_VAR
中获取旧进程打开的监听文件描述符,这个环境变量的值是以冒号隔开的数字,这些数字代表旧进程的文件描述符,在旧进程调用 exec 系函数时传入,问题是这些数字与当前进程有什么关系呢?由于这些文件描述符没有设置FD_CLOEXEC
,所以在 fork 并调用 exec 执行新程序时不会被关闭,也就是和旧进程的文件描述符一致,所以只要知道值就可以拿来用
然后初始化了cycle->listening
数组,数量与旧进程传入的文件描述符相同,这是一个监听对象ngx_listening_t
数组,可以理解为每个监听对象对应一个监听端口,这里将旧进程的fd
直接赋值给监听对象的fd
最后,将ngx_inherited
设置为 1,并调用ngx_set_inherited_sockets
,这个函数太长了,就不贴上来了,大概就是设置了各个监听对象的 socket 地址,socket 类型,接收缓冲区大小,发送缓冲区大小还有一些端口重用等选项,需要注意的是,如果出错会将ignore
的值设置为 1,ngx_cycle_init
中需要使用这个值
0 0
- Nginx 源码阅读笔记4 启动流程
- linux0.11源码阅读笔记1-启动流程-bootsect.s
- Cocos2dx源码阅读 启动流程
- flask源码阅读 应用启动流程
- 源码阅读---AMS与Activity启动流程
- skynet底层源码阅读(8)-启动流程
- Nginx学习笔记(十三):Nginx启动流程
- nginx源码分析—启动流程
- nginx源码分析—启动流程
- nginx源码分析—启动流程
- nginx源码分析—启动流程
- nginx 源码分析阅读笔记-进程管理
- Nginx 源码阅读笔记1 内存池
- Nginx 源码阅读笔记3 时间管理
- Nginx 源码阅读笔记5 初始化 cycle
- Nginx 源码阅读笔记8 epoll 模块
- Android 启动流程: Init.c 阅读笔记
- Android 启动流程: Init.c 阅读笔记
- window bat脚本 变量操作
- spring boot整合shiro引用配置文件配置是出现的问题
- Linux学习笔记11
- 社会浮躁,太多女孩一副娼妓面孔
- Jackcard
- Nginx 源码阅读笔记4 启动流程
- 机器学习入门了解
- IOS Block研究
- Axure8.0 页面上下滑动效果实现
- Ubuntu设置系统时间与网络时间同步
- mybatis增强版(mybatis-plus)
- [centos]给予用户sudo命令的权限
- Optional小小总结
- 面向对象设计原则之依赖倒转原则