zoukankan      html  css  js  c++  java
  • 从 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 进程真正执行的代码函数。

  • 相关阅读:
    面试题:面试题归类 已看1 背1
    面试题:SSH项目总结 !=!=未看 没用
    面试题: 大公司面试 !=!=未看
    Java 重写paint绘图
    c语言中数组的定义和java中数组定义的一些区别
    Java GUI界面补充总结(不定期补充)
    JFrame 的层次结构 及 背景设置说明
    为什么内部类调用的外部变量必须是final修饰的?
    Java Calendar类的使用总结【转】
    String类为什么可以直接赋值
  • 原文地址:https://www.cnblogs.com/zhuwbox/p/3985141.html
Copyright © 2011-2022 走看看