本文我们讲述一下Nginx cycle运行上下文相关的实现。
1. 相关静态函数声明
2. 相关变量定义
下面对各个变量做一个简单的说明:
-
ngx_cycle: 这里ngx_cycle
作为一个全局变量指向nginx当前运行的上下文环境。因为在运行过程中,上下文可能会经常发生变动,因此这里用volatile
修饰。
-
ngx_old_cycles: 保存所有原来到的nginx上下文对象
-
ngx_temp_pool: nginx的一个临时内存池。这是因为在nginx升级过程中,有一些老的ngx_cycle_t *
信息需要保存,这需要空间,因此这里开辟一个临时的内存池来存储这些信息。
-
ngx_cleaner_event: 这里对于ngx_old_cycles
的清理会采用事件机制来完成,因此这里定义一个cleaner event。
-
ngx_test_config: 是否是对nginx配置文件进行测试
-
ngx_dump_config: 是否要dump出nginx配置文件
-
ngx_quiet_mode: 在测试nginx配置文件时,抑制非错误信息的输出
-
dumb: 这里之所以用一个桩dumb
,是因为这里的ngx_event_t
设计主要是针对nginx网络事件的,每个网络事件都关联着一个ngx_connection_t
。这里虽然dumb
是用于定时器事件,但是还是会把该定时器事件看成是一个网络事件来处理,因此要设立一个STUB
来表明这只是一个桩,并不是一个网络事件。
3. 函数ngx_init_cycle()
函数ngx_init_cycle()
很长,下面我们分成几个部分来进行讲解:
3.1 更新当前时间
这里首先更新时区(timezone)
,然后更新时间。这里tp是nginx缓存的最新时间,只所有有tp->sec=0
,主要是跟nginx时间更新的实现策略有关:
可以看到,在sec
相同的情况下,ngx_time_update()
并不会对所有缓存的内容进行更新,这是为了效率方面的考虑。而tp->sec=0
,能够确保实现对所有缓存时间的更新。
3.2 配置文件路径相关初始化
这里首先为新创建的nginx cycle创建一个NGX_CYCLE_POOL_SIZE
(默认16KB)大小的内存池,然后完成配置路径等数据的相关初始化:
-
conf_prefix
: 存放nginx配置文件路径前缀(一般是通过-p
选项来指定nginx的工作路径,然后使用该路径下的配置文件),这里从old_cycle中进行复制。
-
prefix
: nginx路径前缀(后续如日志,pid等都会参考该前缀),这里从old_cycle中进行复制
-
conf_file
: nginx配置文件路径。注意到这里并不是用ngx_pstrdup()
函数来进行复制,这里conf_file符合标准的C语言字符串形式,以\0
方式结尾。从old_cycle中进行复制
-
conf_param
: 存放nginx启动时,通过-g选项传递进来的参数。这里从old_cycle中进行复制
-
paths
: 这里主要是分配一个ngx_path_t *
类型的数组空间,在后续主要是解析配置文件时遇到要打开的文件路径,则把该路径加入到paths
中。
-
config_dump
: 初始化config_dump数组。config_dump
主要是为了在检查nginx配置文件时,将相应的配置dump出来.
-
open_files
: 这里为所有以后要打开的文件(用ngx_open_file_t来抽象)分配链表空间
3.3 共享内存抽象数据结构初始化
这里为存放ngx_shm_zone_t
数据结构,开辟一个链表空间。
3.4 监听结构数组空间初始化
这里为存放ngx_listening_t
数据结构,开辟一个数组空间。另外初始化可复用连接
(一般为http长连接)队列。
3.5 nginx模块相关数据结构初始化
这里首先为cycle->conf_ctx
四级指针分配足够大的空间,来存储各个模块对应的上下文指针; 然后获得当前nginx运行的主机名;再接着为当前cycle
拷贝一份modules指针:
对于核心模块,调用module->create_conf
来创建上下文对象。例如对于上图所示的ngx_core_module
,则会创建出ngx_core_conf_t
上下文。然后将上下文保存到cycle->conf_ctx的对应索引处。
3.7 配置文件初始化
这里首先创建出一个 ngx_conf_t
对象,用于后面解析配置时使用。具体执行步骤如下:
在通过上面的方法解析完相关配置之后,如果是以nginx -t/-T
形式执行,且并未添加-q
参数抑制相关错误输出,则这里调用ngx_conf_parse()函数打印出相关配置文件信息
再接着针对cycle->modules
中的所有核心模块,会调用该核心模块所绑定上下文的init_conf函数指针完成相关的初始化(注意核心模块上下文所用到的数据结构会通过前面讲到的create_conf来完成)。
最后如果ngx_process值为NGX_PROCESS_SIGNALLER
,则到此为止完成了整个nginx cycle的初始化。
3.8 完成相关文件及路径的创建
主要是完成如下事情:
-
pid文件: 如果只是测试配置文件(ngx_test_config),则直接检测能否在指定的位置打开或创建pid文件;如果old_cycle没有初始化,则当前cycle会创建pid文件,并向该文件中写入当前nginx master进程的进程ID。
-
测试锁文件: nginx使用锁机制来实现accept mutex,并且顺序的来访问共享内存。在大多数的系统上,锁都是通过原子操作来实现的,因此会忽略配置文件中的lock_file file指令;而对于其他的一些系统,lock file机制会被使用
默认的lock文件存放位置为logs/nginx.lock
-
创建相应的路径: 这里调用ngx_create_paths()函数来创建相应的路径。在Nginx配置文件中,会配置一些临时路径,在解析配置文件时会将这些路径保存起来,然后在这里统一创建。
-
打开默认的日志文件
-
打开所有配置文件中指定的相关文件,例如http log文件等,并设置FD_CLOEXEC
属性,这使得通过exec调用之后,文件描述符会被关闭
-
设置cycle->log与pool->log
3.9 创建共享内存
这里首先在解析配置文件时,遇到要开设相关的公共缓冲区,就会调用ngx_shared_memory_add()方法将添加到cycle->shared_memory链表中。在这里会根据共享内存name
,size
,tag
以及noreuse
等标志决定是否复用原来老的共享内存还是创建新的共享内存。
3.10 处理listening sockets
这里分两种情况进行处理:
-
如果old_cycle->listening.nelts不为0,则表明有遗留的监听socket。此种情况下分别遍历old_cyle->listening与ngx_cycle->listening,看old_cycle中是否有socket可以复用
-
否则,直接遍历ngx_cycle->listening,设置相关属性
再接着打开cycle->listening中符合条件的socket,然后再对cycle->listening的socket进行配置。
3.11 提交cycle设置
在重新加载配置文件时,调用ngx_init_cycle()
,此时ngx_use_stderr变量为0,调用:
ngx_log_redirect_stderr(cycle);
重定向标准错误。再接着调用ngx_init_modules()完成各个模块的初始化。
3.12 移除old cycle中的共享内存
这里遍历old_cycle->shared_memory与cycle->shared_memory,找出可以删除掉的共享内存。
3.13 关闭不必要的监听socket
这里关闭不能复用的监听socket.
3.14 关闭不必要的打开文件
这里关闭掉old_cycle中的打开文件。
3.15 其他相关收尾操作
在成功创建cycle上下文后,进行一些后续的收尾工作。这里在处理NGX_PROCESS_SINGLE
类型进程时,我们开启定时器来清除old_cycle,这是为了尽快使进程进入工作状态。
3.16 失败情况下现场还原操作
这里在创建新的nginx cycle失败之后,进行相应的现场还原。
[参看]
- nginx源码学习(二)ngx_init_cycle(&init_cycle)函数解析