# Nginx 整体启动流程三
ngx_worker_process_cycle 函数
该函数很简单,就是 master 单进程的事件循环的复刻,只不过这里面涉及到子进程 worker 的属性保存,以及初始化,同时在其中根据 修改 状态来进行流转处理。注意: ngx_process_events_and_timers 为核心函数,该函数我们在之前已经分析过,将会调用 多路复用器完成处理。
ngx_spawn_process(cycle, ngx_worker_process_cycle, (void *) (intptr_t) i, "worker process", type); // 为了方便理解,笔者将创建点放到这里
ngx_pid_t ngx_spawn_process(ngx_cycle_t *cycle, ngx_spawn_proc_pt proc, void *data,
char *name, ngx_int_t respawn){
...
proc(cycle, data); // 可知传入的数据data 为 当前进程的执行下标
...
}
static void ngx_worker_process_cycle(ngx_cycle_t *cycle, void *data)
{
ngx_int_t worker = (intptr_t) data;
ngx_process = NGX_PROCESS_WORKER; // #define NGX_PROCESS_WORKER 3 进程全局变量 标识当前进程类型
ngx_worker = worker; // 进程全局变量 保存当前执行下标
ngx_worker_process_init(cycle, worker); // 初始化工作进程的事件循环,注意:此 cycle 循环为master继承下来的结构
ngx_setproctitle("worker process"); // 设置子进程的名字
for ( ;; ) { // 循环处理事件,直到退出
if (ngx_exiting) { // 检测exiting正在退出标志位,那么检测不存在执行的timer 定时事件,退出worker的事件循环
if (ngx_event_no_timers_left() == NGX_OK) {
ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "exiting");
ngx_worker_process_exit(cycle);
}
}
ngx_log_debug0(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "worker cycle");
ngx_process_events_and_timers(cycle); // 调用模块的多路复用器执行事件(之前已经讲过)
if (ngx_terminate) { // 检测到终止标志位,那么退出worker的事件循环
ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "exiting");
ngx_worker_process_exit(cycle);
}
if (ngx_quit) { // 检测到 quit 退出标志位,那么设置进程名为正在关闭,同时将状态转为 ngx_exiting ,在下一次循环处理时,将进入 ngx_exiting 分支
ngx_quit = 0;
ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0,
"gracefully shutting down");
ngx_setproctitle("worker process is shutting down");
if (!ngx_exiting) {
ngx_exiting = 1;
ngx_set_shutdown_timer(cycle); // 注册关闭定时事件
ngx_close_listening_sockets(cycle); // 关闭 worker 监听的 fd
ngx_close_idle_connections(cycle); // 关闭保存的连接
}
}
if (ngx_reopen) { // reopen 事件 ,用于重新打开 fd(后文分析)
...
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
# ngx_worker_process_init 函数
读者在混沌学堂应该知道:当进程 fork 子进程时,子进程将与父进程共享数据,但当其中一个修改其数据时,将会发生COW机制,所以当 worker 进程 fork 出来的那一刻,事件循环 ngx_cycle_t *cycle 结构的数据将继承父进程的,但我们可以对其中的变量进行重新设置。其他流程自行看注释理解。
static void ngx_worker_process_init(ngx_cycle_t *cycle, ngx_int_t worker)
{
sigset_t set;
ngx_int_t n;
ngx_time_t *tp;
ngx_uint_t i;
ngx_cpuset_t *cpu_affinity;
struct rlimit rlmt;
ngx_core_conf_t *ccf;
ngx_listening_t *ls;
if (ngx_set_environment(cycle, NULL) == NULL) { // 重新设置 worker 的环境变量
exit(2);
}
ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module); // 获取核心模块的配置信息
if (worker >= 0 && ccf->priority != 0) { // 若指定worker优先级,那么设置优先级
if (setpriority(PRIO_PROCESS, 0, ccf->priority) == -1) {
ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
"setpriority(%d) failed", ccf->priority);
}
}
if (ccf->rlimit_nofile != NGX_CONF_UNSET) { // 设置 worker 进程 打开 fd 限制
...
}
if (ccf->rlimit_core != NGX_CONF_UNSET) { // 设置 worker 进程 core 内核转存文件大小限制
...
}
if (geteuid() == 0) { // 当前以root用户执行,那么设置用户组信息
...
}
if (worker >= 0) { // 若指定了将 worker 进程绑定到指定CPU上,避免进程在不同 CPU中迁移导致性能损耗,那么进行CPU绑定操作
cpu_affinity = ngx_get_cpu_affinity(worker);
if (cpu_affinity) {
ngx_setaffinity(cpu_affinity, cycle->log);
}
}
if (ccf->working_directory.len) { // 改变worker进程工作目录
if (chdir((char *) ccf->working_directory.data) == -1) {
ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
"chdir(\"%s\") failed", ccf->working_directory.data);
/* fatal */
exit(2);
}
}
sigemptyset(&set); // 清空信号集
if (sigprocmask(SIG_SETMASK, &set, NULL) == -1) { // 开始响应信号
ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
"sigprocmask() failed");
}
// 更新时间并生成随机数
tp = ngx_timeofday();
srandom(((unsigned) ngx_pid << 16) ^ tp->sec ^ tp->msec);
// 删除 监听套接字 以前的事件,因为此时 worker 进程中根本没有任何事件
ls = cycle->listening.elts;
for (i = 0; i < cycle->listening.nelts; i++) {
ls[i].previous = NULL;
}
for (i = 0; cycle->modules[i]; i++) { // worker 进程 回调模块 init_process 回调函数
if (cycle->modules[i]->init_process) {
if (cycle->modules[i]->init_process(cycle) == NGX_ERROR) {
exit(2);
}
}
}
for (n = 0; n < ngx_last_process; n++) { // 遍历所有进程,关闭他们的 unix socket 通道(为何关闭?因为worker 进程继承了父进程的打开文件,而我们并不需要关心其他进程自己读取数据的fd :ngx_processes[n].channel[1] ,所以直接关闭)
if (ngx_processes[n].pid == -1) { // 进程不存在
continue;
}
if (n == ngx_process_slot) { // 当前进程
continue;
}
if (ngx_processes[n].channel[1] == -1) { // 进程 unix socket 读通道 fd 已经关闭
continue;
}
if (close(ngx_processes[n].channel[1]) == -1) { // 关闭读通道 fd
ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
"close() channel failed");
}
}
if (close(ngx_processes[ngx_process_slot].channel[0]) == -1) { // 关闭当前进程的写通道fd(pass:我自己写我自己?)
ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
"close() channel failed");
}
if (ngx_add_channel_event(cycle, ngx_channel, NGX_READ_EVENT, ngx_channel_handler) // 向事件循环组注册通道读取事件以及事件处理函数 ngx_channel_handler,也即将自己的读通道注册到多路复用器中
== NGX_ERROR)
{
/* fatal */
exit(2);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
# ngx_add_channel_event 函数
该函数用于将事件和通道注册到多路复用器中,我们看到这里首先从连接处获取一个 ngx_connection_t 结构,然后从该结构的属性中获取 ngx_event_t 结构 并 初始化,然后 根据 多路复用器 的类型 选择调用 多路复用器 的 ngx_add_conn 或者 ngx_add_event 方法来注册事件或者连接,我们这里 只研究 epoll,所以后面在讲 EPOLL 时 会直接分析 ngx_add_event 函数指针的回调。pass:连接池的设计部分,我们后文再说。
// 根据宏定义选择 读写事件 对应的多路选择器类型,这里我们关注EPOLL 的宏即可
#elif (NGX_HAVE_EPOLL) && !(NGX_TEST_BUILD_EPOLL)
#define NGX_READ_EVENT (EPOLLIN|EPOLLRDHUP)
#define NGX_WRITE_EVENT EPOLLOUT
ngx_int_t ngx_add_channel_event(ngx_cycle_t *cycle, ngx_fd_t fd, ngx_int_t event,ngx_event_handler_pt handler)
{
ngx_event_t *ev, *rev, *wev;
ngx_connection_t *c;
c = ngx_get_connection(fd, cycle->log); // 从连接池中获取连接结构 ngx_connection_t
if (c == NULL) {
return NGX_ERROR;
}
c->pool = cycle->pool;
rev = c->read; // 连接 读事件
wev = c->write; // 连接 写事件
rev->log = cycle->log;
wev->log = cycle->log;
rev->channel = 1;
wev->channel = 1;
ev = (event == NGX_READ_EVENT) ? rev : wev; // 根据读写类型选择 读事件结构 或者 写事件结构
ev->handler = handler; // 绑定回调函数(当对应事件产生后回调)
// 根据多路复用器类型 调用不同函数
if (ngx_add_conn && (ngx_event_flags & NGX_USE_EPOLL_EVENT) == 0) {
if (ngx_add_conn(c) == NGX_ERROR) {
ngx_free_connection(c);
return NGX_ERROR;
}
} else {
if (ngx_add_event(ev, event, 0) == NGX_ERROR) {
ngx_free_connection(c);
return NGX_ERROR;
}
}
return NGX_OK;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43