如不做特殊说明,本博客所使用的 nginx 源码版本是 1.0.14,[] 中是代码所在的文件!

这一节我们分析ngx_start_worker_processes(),该函数代码比较少,因为它通过调用函数实现功能的,先贴出代码:

[os/unix/ngx_process_cycle.c]

  /* 这是 master 线程调用的、用来生成 worker 进程的入口 */
static void
ngx_start_worker_processes(ngx_cycle_t *cycle, ngx_int_t n, ngx_int_t type)
{
ngx_int_t i;
ngx_channel_t ch; ngx_log_error(NGX_LOG_NOTICE, cycle->log, , "start worker processes"); ch.command = NGX_CMD_OPEN_CHANNEL; /* n 代表要创建多少个 worker 进程 --- 由配置文件中的 worker_process 的值决定的 */
for (i = ; i < n; i++) { /* 多 CPU 获取 CPU 的信息 */
cpu_affinity = ngx_get_cpu_affinity(i);
/* 创建进程函数 */
/* ngx_spawn_process() 在 OS/ngx_process.h/c 文件中定义 ,ngx_worker_process_cycle() 是新建进程要执行的函数 */
ngx_spawn_process(cycle, ngx_worker_process_cycle, NULL,
"worker process", type); ch.pid = ngx_processes[ngx_process_slot].pid;
ch.slot = ngx_process_slot;
ch.fd = ngx_processes[ngx_process_slot].channel[]; /* 用来干嘛? */
ngx_pass_open_channel(cycle, &ch);
}
}

  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]

  ngx_pid_t
ngx_spawn_process(ngx_cycle_t *cycle, ngx_spawn_proc_pt proc, void *data,
char *name, ngx_int_t respawn)
{
u_long on;
ngx_pid_t pid; ngx_int_t s; if (respawn >= ) {
s = respawn; } else { for (s = ; s < ngx_last_process; s++) {
if (ngx_processes[s].pid == -) {
break;
}
} if (s == NGX_MAX_PROCESSES) {
ngx_log_error(NGX_LOG_ALERT, cycle->log, ,
"no more than %d processes can be spawned",
NGX_MAX_PROCESSES);
return NGX_INVALID_PID;
}
} if (respawn != NGX_PROCESS_DETACHED) { /* Solaris 9 still has no AF_LOCAL */
/* 创建一对 socket 描述符存放在变量 ngx_process[s].channel 中*/
if (socketpair(AF_UNIX, SOCK_STREAM, , ngx_processes[s].channel) == -)
{
ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
"socketpair() failed while spawning \"%s\"", name);
return NGX_INVALID_PID;
} ngx_log_debug2(NGX_LOG_DEBUG_CORE, cycle->log, ,
"channel %d:%d",
ngx_processes[s].channel[],
ngx_processes[s].channel[]); /* 这里将 channel[0],channel[1] 看着是管道的两端 */
/* 将 channel[0] 设置为非阻塞 */
if (ngx_nonblocking(ngx_processes[s].channel[]) == -) {
ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
ngx_nonblocking_n " failed while spawning \"%s\"",
name);
ngx_close_channel(ngx_processes[s].channel, cycle->log);
return NGX_INVALID_PID;
} /* 将 channel[1] 设置为非阻塞 */
if (ngx_nonblocking(ngx_processes[s].channel[]) == -) {
ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
ngx_nonblocking_n " failed while spawning \"%s\"",
name);
ngx_close_channel(ngx_processes[s].channel, cycle->log);
return NGX_INVALID_PID;
} on = ;
if (ioctl(ngx_processes[s].channel[], FIOASYNC, &on) == -) {
ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
"ioctl(FIOASYNC) failed while spawning \"%s\"", name);
ngx_close_channel(ngx_processes[s].channel, cycle->log);
return NGX_INVALID_PID;
} /* F_SETOWN 设置接受 SIGIO 和 SIGURG 信号的进程 ID 或 进程组 ID */
if (fcntl(ngx_processes[s].channel[], F_SETOWN, ngx_pid) == -) {
ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
"fcntl(F_SETOWN) failed while spawning \"%s\"", name);
ngx_close_channel(ngx_processes[s].channel, cycle->log);
return NGX_INVALID_PID;
} if (fcntl(ngx_processes[s].channel[], F_SETFD, FD_CLOEXEC) == -) {
ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
"fcntl(FD_CLOEXEC) failed while spawning \"%s\"",
name);
ngx_close_channel(ngx_processes[s].channel, cycle->log);
return NGX_INVALID_PID;
} if (fcntl(ngx_processes[s].channel[], F_SETFD, FD_CLOEXEC) == -) {
ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
"fcntl(FD_CLOEXEC) failed while spawning \"%s\"",
name);
ngx_close_channel(ngx_processes[s].channel, cycle->log);
return NGX_INVALID_PID;
} ngx_channel = ngx_processes[s].channel[]; } else {
ngx_processes[s].channel[] = -;
ngx_processes[s].channel[] = -;
} /* 设置工作进程的下标 */
ngx_process_slot = s; /* 创建进程 */
pid = fork(); switch (pid) { /* 创建进程失败 */
case -:
ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
"fork() failed while spawning \"%s\"", name);
ngx_close_channel(ngx_processes[s].channel, cycle->log);
return NGX_INVALID_PID;
/* 如果是父进程 */
case :
ngx_pid = ngx_getpid(); proc(cycle, data);
break; default:
break;
} ngx_log_error(NGX_LOG_NOTICE, cycle->log, , "start %s %P", name, pid); ngx_processes[s].pid = pid;
ngx_processes[s].exited = ; if (respawn >= ) {
return pid;
} ngx_processes[s].proc = proc;
ngx_processes[s].data = data;
ngx_processes[s].name = name;
ngx_processes[s].exiting = ; switch (respawn) { case NGX_PROCESS_NORESPAWN:
ngx_processes[s].respawn = ;
ngx_processes[s].just_spawn = ;
ngx_processes[s].detached = ;
break; case NGX_PROCESS_JUST_SPAWN:
ngx_processes[s].respawn = ;
ngx_processes[s].just_spawn = ;
ngx_processes[s].detached = ;
break; case NGX_PROCESS_RESPAWN:
ngx_processes[s].respawn = ;
ngx_processes[s].just_spawn = ;
ngx_processes[s].detached = ;
break; case NGX_PROCESS_JUST_RESPAWN:
ngx_processes[s].respawn = ;
ngx_processes[s].just_spawn = ;
ngx_processes[s].detached = ;
break; case NGX_PROCESS_DETACHED:
ngx_processes[s].respawn = ;
ngx_processes[s].just_spawn = ;
ngx_processes[s].detached = ;
break;
} if (s == ngx_last_process) {
ngx_last_process++;
} return pid;
}

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

最新文章

  1. matlab练习程序(矩形变换为单连通形状)
  2. 将excel的.xlsx文件转成数据库文件.db的方法
  3. IE6、7下inline-block不起作用
  4. ThinkPHP第十七天(隐藏index.php和简短路径配置)
  5. 《JAVASCRIPT高级程序设计》Ajax与Comet
  6. 从C#到TypeScript - function
  7. ASP.NET Core文件上传与下载(多种上传方式)
  8. JavaScript笔记整理
  9. 题解-hdu2866 Special Prime
  10. (素材源代码)猫猫学IOS(四)UI之半小时搞定Tom猫
  11. LOJ 534 花团(线段树+dfs栈)
  12. C语言实现哈夫曼编码(最小堆,二叉树)
  13. Android比较实用的性能优化
  14. Nginx访问日志 Nginx日志切割 静态文件不记录日志和过期时间
  15. WINDOWS消息和窗口简介
  16. java IO字符流
  17. 在Linux中连接android设备
  18. 调整linux系统时区
  19. BZOJ5217: [Lydsy2017省队十连测]航海舰队 FFT
  20. 使用docker exec命令

热门文章

  1. Mybatis下配置调用Oracle自定义函数返回的游标结果集
  2. HTML5的你应该记住的一些知识点
  3. NLog 安装使用
  4. 使用AsyncHttpClient碰到的问题及解决方法
  5. iOS 使用GBK编码的hmacMD5算法
  6. 使用switch case语句来显示月份的对应天数
  7. 100. Same Tree(C++)
  8. ACM hdu 1008 Elavator
  9. PHP 单一入口
  10. ubuntu14.04.1 LTS 64bits较快的更新源