从 mian 函数开始一步一步分析 nginx 执行流程(三)

如不做特殊说明,本博客所使用的 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 }
ngx_spawn_process()函数代码

  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 进程真正执行的代码函数。

原文地址:https://www.cnblogs.com/zhuwbox/p/3985141.html