如不做特殊说明,本博客所使用的 nginx 源码版本是 1.0.14,[] 中是代码所在的文件!
这一节我们分析ngx_start_worker_processes(),该函数代码比较少,因为它通过调用函数实现功能的,先贴出代码:
[os/unix/ngx_process_cycle.c]
377 /* 这是 master 线程调用的、用来生成 worker 进程的入口 */ 378 static void 379 ngx_start_worker_processes(ngx_cycle_t *cycle, ngx_int_t n, ngx_int_t type) 380 { 381 ngx_int_t i; 382 ngx_channel_t ch; 383 384 ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "start worker processes"); 385 386 ch.command = NGX_CMD_OPEN_CHANNEL; 387 388 /* n 代表要创建多少个 worker 进程 --- 由配置文件中的 worker_process 的值决定的 */ 389 for (i = 0; i < n; i++) { 390 391 /* 多 CPU 获取 CPU 的信息 */ 392 cpu_affinity = ngx_get_cpu_affinity(i); 393 /* 创建进程函数 */ 394 /* ngx_spawn_process() 在 OS/ngx_process.h/c 文件中定义 ,ngx_worker_process_cycle() 是新建进程要执行的函数 */ 395 ngx_spawn_process(cycle, ngx_worker_process_cycle, NULL, 396 "worker process", type); 397 398 ch.pid = ngx_processes[ngx_process_slot].pid; 399 ch.slot = ngx_process_slot; 400 ch.fd = ngx_processes[ngx_process_slot].channel[0]; 401 402 /* 用来干嘛? */ 403 ngx_pass_open_channel(cycle, &ch); 404 } 405 }
1. 我们可以看到,该函数接受三个参数,分别是全局变量 cycle,要创建 worker 进程的个数 n, 要创建进程的类型 type,type有以下 5 个值。
a. NGX_PROCESS_RESPAWN
b. NGX_PROCESS_NORESPAWN
c. NGX_PROCESS_JUST_SPAWN
d. NGX_PROCESS_JUST_RESPAWN
e. NGX_PROCESS_DETACHED
type 的值将影响进程结构体 ngx_process_t 的 respawn、detached、just_spawn 标志位的值。
2. 386 行设置 ngx_channerl_t 结构体 command 的变量为 NGX_CMD_OPEN_CHANNEL,暂时还没分析到这个 channel,我猜测是意味着打开 channel,开始进行父子进程间的通信了。
3. 389-404 行通过 for 循环创建 n 个 worker 进程,其中最终要的就是 ngx_spawn_process() 函数了,该函数真正用来创建进程。我慢来看一些 ngx_spawn_process() 的代码:
[os/unix/ngx_process.c]
86 ngx_pid_t 87 ngx_spawn_process(ngx_cycle_t *cycle, ngx_spawn_proc_pt proc, void *data, 88 char *name, ngx_int_t respawn) 89 { 90 u_long on; 91 ngx_pid_t pid; 92 93 ngx_int_t s; 94 95 96 if (respawn >= 0) { 97 s = respawn; 98 99 } else { 100 101 for (s = 0; s < ngx_last_process; s++) { 102 if (ngx_processes[s].pid == -1) { 103 break; 104 } 105 } 106 107 108 if (s == NGX_MAX_PROCESSES) { 109 ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, 110 "no more than %d processes can be spawned", 111 NGX_MAX_PROCESSES); 112 return NGX_INVALID_PID; 113 } 114 } 115 116 if (respawn != NGX_PROCESS_DETACHED) { 117 118 /* Solaris 9 still has no AF_LOCAL */ 119 /* 创建一对 socket 描述符存放在变量 ngx_process[s].channel 中*/ 120 if (socketpair(AF_UNIX, SOCK_STREAM, 0, ngx_processes[s].channel) == -1) 121 { 122 ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, 123 "socketpair() failed while spawning \"%s\"", name); 124 return NGX_INVALID_PID; 125 } 126 127 ngx_log_debug2(NGX_LOG_DEBUG_CORE, cycle->log, 0, 128 "channel %d:%d", 129 ngx_processes[s].channel[0], 130 ngx_processes[s].channel[1]); 131 132 /* 这里将 channel[0],channel[1] 看着是管道的两端 */ 133 /* 将 channel[0] 设置为非阻塞 */ 134 if (ngx_nonblocking(ngx_processes[s].channel[0]) == -1) { 135 ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, 136 ngx_nonblocking_n " failed while spawning \"%s\"", 137 name); 138 ngx_close_channel(ngx_processes[s].channel, cycle->log); 139 return NGX_INVALID_PID; 140 } 141 142 /* 将 channel[1] 设置为非阻塞 */ 143 if (ngx_nonblocking(ngx_processes[s].channel[1]) == -1) { 144 ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, 145 ngx_nonblocking_n " failed while spawning \"%s\"", 146 name); 147 ngx_close_channel(ngx_processes[s].channel, cycle->log); 148 return NGX_INVALID_PID; 149 } 150 151 on = 1; 152 if (ioctl(ngx_processes[s].channel[0], FIOASYNC, &on) == -1) { 153 ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, 154 "ioctl(FIOASYNC) failed while spawning \"%s\"", name); 155 ngx_close_channel(ngx_processes[s].channel, cycle->log); 156 return NGX_INVALID_PID; 157 } 158 159 /* F_SETOWN 设置接受 SIGIO 和 SIGURG 信号的进程 ID 或 进程组 ID */ 160 if (fcntl(ngx_processes[s].channel[0], F_SETOWN, ngx_pid) == -1) { 161 ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, 162 "fcntl(F_SETOWN) failed while spawning \"%s\"", name); 163 ngx_close_channel(ngx_processes[s].channel, cycle->log); 164 return NGX_INVALID_PID; 165 } 166 167 if (fcntl(ngx_processes[s].channel[0], F_SETFD, FD_CLOEXEC) == -1) { 168 ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, 169 "fcntl(FD_CLOEXEC) failed while spawning \"%s\"", 170 name); 171 ngx_close_channel(ngx_processes[s].channel, cycle->log); 172 return NGX_INVALID_PID; 173 } 174 175 if (fcntl(ngx_processes[s].channel[1], F_SETFD, FD_CLOEXEC) == -1) { 176 ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, 177 "fcntl(FD_CLOEXEC) failed while spawning \"%s\"", 178 name); 179 ngx_close_channel(ngx_processes[s].channel, cycle->log); 180 return NGX_INVALID_PID; 181 } 182 183 ngx_channel = ngx_processes[s].channel[1]; 184 185 } else { 186 ngx_processes[s].channel[0] = -1; 187 ngx_processes[s].channel[1] = -1; 188 } 189 190 /* 设置工作进程的下标 */ 191 ngx_process_slot = s; 192 193 /* 创建进程 */ 194 pid = fork(); 195 196 switch (pid) { 197 198 /* 创建进程失败 */ 199 case -1: 200 ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, 201 "fork() failed while spawning \"%s\"", name); 202 ngx_close_channel(ngx_processes[s].channel, cycle->log); 203 return NGX_INVALID_PID; 204 /* 如果是父进程 */ 205 case 0: 206 ngx_pid = ngx_getpid(); 207 208 proc(cycle, data); 209 break; 210 211 default: 212 break; 213 } 214 215 ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "start %s %P", name, pid); 216 217 ngx_processes[s].pid = pid; 218 ngx_processes[s].exited = 0; 219 220 if (respawn >= 0) { 221 return pid; 222 } 223 224 ngx_processes[s].proc = proc; 225 ngx_processes[s].data = data; 226 ngx_processes[s].name = name; 227 ngx_processes[s].exiting = 0; 228 229 switch (respawn) { 230 231 case NGX_PROCESS_NORESPAWN: 232 ngx_processes[s].respawn = 0; 233 ngx_processes[s].just_spawn = 0; 234 ngx_processes[s].detached = 0; 235 break; 236 237 case NGX_PROCESS_JUST_SPAWN: 238 ngx_processes[s].respawn = 0; 239 ngx_processes[s].just_spawn = 1; 240 ngx_processes[s].detached = 0; 241 break; 242 243 case NGX_PROCESS_RESPAWN: 244 ngx_processes[s].respawn = 1; 245 ngx_processes[s].just_spawn = 0; 246 ngx_processes[s].detached = 0; 247 break; 248 249 case NGX_PROCESS_JUST_RESPAWN: 250 ngx_processes[s].respawn = 1; 251 ngx_processes[s].just_spawn = 1; 252 ngx_processes[s].detached = 0; 253 break; 254 255 case NGX_PROCESS_DETACHED: 256 ngx_processes[s].respawn = 0; 257 ngx_processes[s].just_spawn = 0; 258 ngx_processes[s].detached = 1; 259 break; 260 } 261 262 if (s == ngx_last_process) { 263 ngx_last_process++; 264 } 265 266 return pid; 267 }
3.1. 该函数代码太长,我将它折叠!接下来我们一步步分析。先看96-114行代码。如果 respawn(是否是重新生成子进程)大于等于 0(如果大于等于 0 ,那说明 type 不是上面说到的 5 中类型之一,因为上面个的 5 个类型的宏定义全是负值见[os/unix/ngx_process.h]。此时的 respawn 应该是作为要重新被生成的进程在 ngx_process 数组中的下标),那么 s 就等于 respawn, s 是即将要被创建的进程在进程数组 ngx_process 中的下标。如果 respawn 小于 0,那么就要在 ngx_process 数组中找到一个可用的空间,用来保存即将要被创建的进程的信息。 108-113 行表明创建的进程数已经达到设置的能创建的最大进程数---NGX_MAX_PROCESS,那么 ngx_spawn_process 创建进程失败。
3.2. 如果 raspawn 类型是 NGX_PROCESS_DETACHED 那么意味着创建的进程和 master 进程没有父子关系,则设置该进程的 channel[0] 和 channel[1] 的值为 -1(相当于管道的两端都关闭),即子进程不与父进程进行通信。如果是 NGX_PROCESS_DETACHED 意外的其他类型,120 行通过调用 socketpair() 函数创建用于父子间通信的匿名已经连接的套接字(理解为匿名管道也没错哈),如果失败,则返回错误!
3.3. 134-182 都是在设置创建的套接字两端的属性。134 行和 143 行分别将套接字两端 (channel[0] 和 channel[1]表示) 设置为 non-blocking 。152 行通过 ioctl 函数设置针对 channle[0] 端信号异步 I/O 标志(它决定是否收取针对本套接口的异步 I/O 信号(SIGIO))。这里设置为接受针对该套接口(channel[0])的异步I/O信号(SIGIO)。160 行设置 channel[0] 端接收 SIGIO 和 SIGURG 信号的进程 ID 或进程组ID,这里设置的进程 ID 是 nginx 的 master 进程。 167 和 175 行设置 channel[0] 端和 channel[1] 端的描述符标志为 FD_CLOSEXEC。
3.4. 191 将 s 复制给 ngx_process_slot。194 调用 fork() 创建进程。如果创建失败,ngx_spawn_process 结束,返回 ngx_invalid_pid 错误
3.5. 205 如果是子进程(新创建的 wroker 进程),则调用 pro 函数,注意:该函数是通过 ngx_spawn_process() 函数传过来的变量 proc,他是一个函数指针,该变量值是 ngx_worker_process_cycle [os/unix/ngx_process_cycle.c]是一个函数,是 worker 进程要执行的函数。这个函数是 worker 进程的重点。我们下一节会分析该函数。
3.6. 显然 215-该函数结束都是父进程继续执行的代码( worker 进程已经进入 proc 函数运行了)。224-260 都是保存新创建的进程信息到 ngx_process[i] 。
3.7. 262-264 实现ngx_last_process 的自增。到这里,ngx_spawn_process() 函数执行完毕了!
4. 398-400 保存子进程的 pid ,进程在 ngx_prcess 数组中的下标和子进程的 channel[0] 端( )到 ch。
5. 403行通过调用 ngx_pass_open_channel() 传递与新创建进程的 ch 到其他 worker 进程。---待分析,可能 worker 之间也要进行通信!
6. 至此ngx_process_cycle 函数分析完毕,下一节我们将分析 ngx_worker_process_cycle() 函数---一个 worker 进程真正执行的代码函数。