nginx创建n个工作子进程
nginx属于多进程方案,那么是怎么创建n个进程的呢??
1.1 linux中fork创建进程
linux下创建进程的系统调用是fork。其定义如下
#include <sys/types.h>
#include <unistd.h>
pid_t fork();
在循环中创建进程时,进程的个数是怎样的?
1、循环中没有任何父子进程的判断
#include <sys/types.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
int main()
{
pid_t pid;
int idx = -1;
for (int i = 0; i < 2; i++) {
pid = fork();
}
while (1);
exit(0) ;
}
输出为
可以看出总共有4个进程
其生成过程图如下所示
2、创建指定个数的子进程
在创建子进程后,根据fork的返回值判断是否是子进程,如果是子进程:
(1)退出循环,进入子进程的事件处理;(2)不退出循环,直接是子进程的事件处理,其中 事件处理是一个循环,所在循环退出前,子进程是不会执行for语句的,这也是nginx创建工作进程的方式
代码如下
int main()
{
pid_t pid;
int idx = -1;
for (int i = 0; i < 2; i++) {
pid = fork();
if (pid < 0) {
exit(-1);
} else if (pid > 0) {
continue;
} else {
idx = i;
while (1) {
printf("idx=%d\n", idx);
}
}
}
while (1);
exit(0) ;
}
输出为
可以看到有3个进程,两个子进程,一个父进程
其生成过程图如下所示
2.nginx中创建n个进程
nginx创建work进程是在一个循环中进行的;
static void ngx_start_worker_processes(ngx_cycle_t *cycle, ngx_int_t n, ngx_int_t type)
{
for (i = 0; i < n; i++) {
//省略
ngx_spawn_process(cycle, ngx_worker_process_cycle, NULL,
"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)
{
//省略若干
pid = fork();
switch (pid) {
case -1:
//省略若干
case 0:
ngx_pid = ngx_getpid();
proc(cycle, data);
break;
default:
break;
}
//省略若干
}
我们知道,fork在循环中创建子进程的个数是与循环个数n不等的,例如:
for(int i = 0; i < 3; ++i)
{
pid = fork();
if(pid > 0)
{
//父进程
}
else if(pid == 0)
{
//子进程
}
else
{
//error
}
}
上面的代码运行后,总共的进程数为2的3次方个,即8个进程;
那么问题来了,nginx也是通过for来创建work进程,如何控制work进程数目的呢?一值比较疑惑,通过仔细检查代码,发现原因这样的;
nginx在fork后,会运行 proc(cycle, data); proc就是ngx_worker_process_cycle函数;
static void
ngx_worker_process_cycle(ngx_cycle_t *cycle, void *data)
{
for ( ;; ) {
//省略若干
ngx_worker_process_exit(cycle);
//省略若干
}
}
在这个函数中,如果退出,最终会调用ngx_worker_process_exit(cycle);
static void
ngx_worker_process_exit(ngx_cycle_t *cycle)
{
//省略若干
exit(0);
}
看到没有,这个函数最后会调用exit(0); 直接退出进程;所以整个过程明了了;
总结:
nginx通过fork循环来创建进程,是如何控制work进程数目,而没有出现进程泛滥,达到2的n次方的进程数目呢?
原因:在fork完work进程后,调用work进程的死循环函数,在死循环中,如果收到quit命令,会直接exit(),不会在work进程中再次执行fork函数,所以子进程不会在创建子进程。