zoukankan      html  css  js  c++  java
  • 从 mian 函数开始一步一步分析 nginx 执行流程(二)

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

    上一个博客中我们将 main 函数执行流程分析完,到最后一步调用 ngx_master_process_cycle(),现在我们就来分析该函数的执行流程,写贴部分代码:

    [os/unix/ngx_process_cycle.c]

      83 /* master 进程:监控进程 */
      84 void
      85 ngx_master_process_cycle(ngx_cycle_t *cycle)
      86 {
      87     char              *title;
      88     u_char            *p;
      89     size_t             size;
      90     ngx_int_t          i;
      91     ngx_uint_t         n, sigio;
      92     sigset_t           set;
      93     struct itimerval   itv;
      94     ngx_uint_t         live;
      95     ngx_msec_t         delay;
      96     ngx_listening_t   *ls;
      97     ngx_core_conf_t   *ccf;
      98 
      99     /* master 进程是靠信号来控制的,所以以下可以看着是对信号的一些设置 */
     100     /* sigemptyset() 用来将参数信号集初始化并清空 */
     101     sigemptyset(&set);
     102     /* sigaddset()用来将参数signum 代表的信号加入至参数set 信号集里。*/
     103     sigaddset(&set, SIGCHLD);   /* 加入 SIGCHILD 信号 */
     104     sigaddset(&set, SIGALRM);
     105     sigaddset(&set, SIGIO);
     106     sigaddset(&set, SIGINT);
     107     sigaddset(&set, ngx_signal_value(NGX_RECONFIGURE_SIGNAL));
     108     sigaddset(&set, ngx_signal_value(NGX_REOPEN_SIGNAL));
     109     sigaddset(&set, ngx_signal_value(NGX_NOACCEPT_SIGNAL));
     110     sigaddset(&set, ngx_signal_value(NGX_TERMINATE_SIGNAL));
     111     sigaddset(&set, ngx_signal_value(NGX_SHUTDOWN_SIGNAL));
     112     sigaddset(&set, ngx_signal_value(NGX_CHANGEBIN_SIGNAL));
     113 
     114     /* 阻塞信号? */
     115     if (sigprocmask(SIG_BLOCK, &set, NULL) == -1) {
     116         ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
     117                       "sigprocmask() failed");
     118     }
     119 
     120     sigemptyset(&set);

      1.由于 master 进程是控制进程,他不做实际的工作,只是实现对 worker 进程的控制。所以 master 会一直阻塞起来,唯一驱动 master 进程进行工作的是信号! 101 - 12 行相当于把想要关心的信号都加入到 set 中。

     123     size = sizeof(master_process);
     124 
     125     for (i = 0; i < ngx_argc; i++) {
     126         size += ngx_strlen(ngx_argv[i]) + 1;
     127     }
     128 
     129     title = ngx_pnalloc(cycle->pool, size);
     130 
     131     p = ngx_cpymem(title, master_process, sizeof(master_process) - 1);
     132     for (i = 0; i < ngx_argc; i++) {
     133         *p++ = ' ';
     134         p = ngx_cpystrn(p, (u_char *) ngx_argv[i], size);
     135     }
     136 
     137     ngx_setproctitle(title);
     138 
     139     /* 获取配置信息 */
     140     ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module);
     141 
     142     /* 创建工作进程:worker_process ,这是重要的一步 */
     143     ngx_start_worker_processes(cycle, ccf->worker_processes,
     144                                NGX_PROCESS_RESPAWN);
     145     /* 运行 Cache 进程 :该函数中还可能会创建另一个进程 */
     146     ngx_start_cache_manager_processes(cycle, 0);
     147 
     148     /* 全部是旗标 */
     149     ngx_new_binary = 0; /* 新的二进制文件 */
     150     delay = 0;          /* 是否延时      */
     151     sigio = 0;          /* IO信号        */
     152     live = 1;           /* 是否活着      */

      2. 12-137 是为了设置进程的名称,该进程的名称格式是 "nginx: master process "+ 你启动 nginx 所输入的命令。例如我输入 sudo ./nginx -g aaa 启动 nginx,那么 master 进程名就是 : nginx: master process ./nginx -g -aaa

      3. 140 行获取配置信息

      4. 143 行很重要,调用 ngx_start_process () 函数创建 worker 进程,真正的工作也就从这里开始了,下一节我们将分析该函数。146 行调用 ngx_start_cache_manager_processes() 函数创建管理进程!这个函数我们以后也会进行相应的分析。

      5. 149-152 行初始化一些全局变量 ngx_new_binary 是否执行新的二进制文件、delay 是否时延、sigio 是否有 IO 信号、live 是否存活

     154     /* master 进程开始了,这里是个死循环 */
     155     for ( ;; ) {
     156         if (delay) {
     157             if (ngx_sigalrm) {
     158                 sigio = 0;
     159                 delay *= 2;
     160                 ngx_sigalrm = 0;
     161             }
     162 
     163             ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
     164                            "termination cycle: %d", delay);
     165 
     166             itv.it_interval.tv_sec = 0;
     167             itv.it_interval.tv_usec = 0;
     168             itv.it_value.tv_sec = delay / 1000;
     169             itv.it_value.tv_usec = (delay % 1000 ) * 1000;
     170 
     171             //ÉèÖöšÊ±Æ÷
     172             if (setitimer(ITIMER_REAL, &itv, NULL) == -1) {
     173                 ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
     174                               "setitimer() failed");
     175             }
     176         }
     177 
     178         ngx_log_debug0(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "sigsuspend");
     179 
     180         /* 通常情况下,master 进程挂起,等待信号来临 */
     181         sigsuspend(&set);
     182         /* 信号来了,往下执行 */
     183         ngx_time_update();
     184 
     185         ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
     186                        "wake up, sigio %i", sigio);
     187         /* 
     188             以下变量 ngx_reap、ngx_terminate、ngx_quit、ngx_reopen、ngx_restart、ngx_reconfigure、ngx_change_binary、ngx_noaccept
     189             都是通过信号量控制的 
     190          */
     191         /* 有子进程退出吗? */
     192         if (ngx_reap) {
     193             ngx_reap = 0;
     194             ngx_log_debug0(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "reap children");
     195 
     196             /*   */
     197             live = ngx_reap_children(cycle);
     198         }
     199 
     200         /* live 表示是不是所有子进程正常退出 */
     201         if (!live && (ngx_terminate || ngx_quit)) {
     202             ngx_master_process_exit(cycle);
     203         }
     204 
     205         /* 进程退出或终止---简单而粗暴 */
     206         if (ngx_terminate) {
     207 
     208             if (delay == 0) {
     209                 delay = 50;
     210             }
     211 
     212             if (sigio) {
     213                 sigio--;
     214                 continue;
     215             }
     216  
     217             sigio = ccf->worker_processes + 2 /* cache processes */;
     218 
     219             if (delay > 1000) {
     220 
     221                 ngx_signal_worker_processes(cycle, SIGKILL);
     222             } else {
     223 
     224                 ngx_signal_worker_processes(cycle,
     225                                        ngx_signal_value(NGX_TERMINATE_SIGNAL));
     226             }
     227 
     228             continue;
     229         }
     230 
     231         /* 进程退出或终止---优雅的退出 */
     232         if (ngx_quit) {
     233 
     234             ngx_signal_worker_processes(cycle,
     235                                         ngx_signal_value(NGX_SHUTDOWN_SIGNAL));
     236 
     237             ls = cycle->listening.elts;
     238             for (n = 0; n < cycle->listening.nelts; n++) {
     239                 if (ngx_close_socket(ls[n].fd) == -1) {
     240                     ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_socket_errno,
     241                                   ngx_close_socket_n " %V failed",
     242                                   &ls[n].addr_text);
     243                 }
     244             }
     245             cycle->listening.nelts = 0;
     246 
     247             continue;
     248         }
     249 
     250         /* 重新加载配置文件 */
     251         if (ngx_reconfigure) {
     252             ngx_reconfigure = 0;
     253 
     254             if (ngx_new_binary) {
     255                 ngx_start_worker_processes(cycle, ccf->worker_processes,
     256                                            NGX_PROCESS_RESPAWN);
     257                 ngx_start_cache_manager_processes(cycle, 0);
     258                 ngx_noaccepting = 0;
     259 
     260                 continue;
     261             }
     261             }
     262 
     263             ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "reconfiguring");
     264 
     265             cycle = ngx_init_cycle(cycle);
     266             if (cycle == NULL) {
     267                 cycle = (ngx_cycle_t *) ngx_cycle;
     268                 continue;
     269             }
     270 
     271             ngx_cycle = cycle;
     272             ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx,
     273                                                    ngx_core_module);
     274             ngx_start_worker_processes(cycle, ccf->worker_processes,
     275                                        NGX_PROCESS_JUST_RESPAWN);
     276             ngx_start_cache_manager_processes(cycle, 1);
     277             live = 1;
     278             ngx_signal_worker_processes(cycle,
     279                                         ngx_signal_value(NGX_SHUTDOWN_SIGNAL));
     280         }
     281 
     282         /* 重新启动 */
     283         if (ngx_restart) {
     284             ngx_restart = 0;
     285             ngx_start_worker_processes(cycle, ccf->worker_processes,
     286                                        NGX_PROCESS_RESPAWN);
     287             ngx_start_cache_manager_processes(cycle, 0);
     288             live = 1;
     289         }
     290 
     291         /* 重新打开 */
     292         if (ngx_reopen) {
     293             ngx_reopen = 0;
     294             ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "reopening logs");
     295             ngx_reopen_files(cycle, ccf->user);
     296             ngx_signal_worker_processes(cycle,
     297                                         ngx_signal_value(NGX_REOPEN_SIGNAL));
     298         }
     299 
     300         /* 改变执行的二进制文件 */
     301         if (ngx_change_binary) {
     302             ngx_change_binary = 0;
     303             ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "changing binary");
     304             //œøÐÐÈÈŽúÂëÌæ»»£¬ÕâÀïÊǵ÷ÓÃexecveÀŽÖŽÐÐеĎúÂë
     305             ngx_new_binary = ngx_exec_new_binary(cycle, ngx_argv);
     306         }
     307 
     308         /* 不接受 */
     309         if (ngx_noaccept) {
     310             ngx_noaccept = 0;
     311             ngx_noaccepting = 1;
     312             ngx_signal_worker_processes(cycle,
     313                                         ngx_signal_value(NGX_SHUTDOWN_SIGNAL));
     314         }
     315     }
     316 }

      6. 以上全部代码是一个死循环,也就是说 master 进程就在这个循环中不停的执行着。

      7. 156-176 行,如果设置了时延,就用系统函数 setittimer 设置定时。

      8.  看 181 行用 sigsuspend 函数将整个进程挂起,进程交出 cpu ,进程进入休眠状态

      9.  只有在进程收到信号之后才会往下执行,183 行更新当前时间当前时间(当然要更新时间了,等进程收到信号继续往下进行的时候都不知道过了多久了)!

      10. 当进程收到信号时会执行相应的回调函数设置一些全局变量的值。我们知道,在(一)的分析中有 ngx_init_signals() 初始化了信号,而这个函数所做的工作就是设置了每种信号的回调函数。这些信号的回调函数要么是空,要么是系统函数,例如 SIG_IGN(忽略信号),要么就是作者自己定义的函数 void ngx_signal_handler(int signo)[os/unix/ngx_proces.c] 该函数代码较长,这里不贴出,大家自行查看。该函数的唯一一个参数就是接受到的信号值,根据信号值设置一些全局变量值。

      11. 192 行判断是否有子进程退出,如果有的话,通过调用 ngx_reap_children() 来设置 live 的值。如果 live = 1 表示子进程不是正常退出,否则表示正常退出!

      12. 201 行如果 ngx_ternubate 或者 ngx_quit  为 1,并且 live 为 0 则调用 ngx_master_process_exit() 函数退出 master 进程!该退出函数所做的事有

        a. 删除之前生成的 pid 文件

        b. 执行所有模块的 exit_master 函数回调

        c. 关闭所有监听套接字

        d. 销毁内存池 

      14. 206 行判断进程是否终止,这是一种简单而又粗暴的退出,232 行判断进程是否终止,这中方法很优雅,做一些关闭前的准备工作。

      15. 251 行判断是否重新加载配置文件,如果是的话,就重新加载配置文件,重新设置全局变量 cycle,重新创建 worker 进程和 cache manager 进程(调用 ngx_start_worker_processes() 和 ngx_start_cache_manager_processes() )。

      16. 283 行判断是否重启程序,如果是就重新创建 worker 进程和 cache manager 进程(调用 ngx_start_worker_processes() 和 ngx_start_cache_manager_processes() )

      17. 292 行判断是否重新打开日志,如果是的话,调用 ngx_reopen_files 打开日志文件,并且将该信号发送给 worker 进程

      18. 301 行判断是否改变执行的二进制文件,如果是就调用ngx_exec_new_binary() 函数执行新的二进制文件

      19. 311 行判断是否接受 connections(连接),如果是的话调用  ngx_signal_worker_processes() 将shutdown 信号传给 worker 进程。

      20. 至此  ngx_master_process_cycle() 分析完毕!在上面的 for 循环里面多次调用了 ngx_signal_worker_processes() 函数,该函数如则将 master 进程收到的信号发送给 worker 进程( master 进程的子进程). 该函数的代码如下:

      

     492 /* 传递信号给 worker 进程 */
     493 static void
     494 ngx_signal_worker_processes(ngx_cycle_t *cycle, int signo)
     495 {
     496     ngx_int_t      i;
     497     ngx_err_t      err;
     498     ngx_channel_t  ch;
     499 
     500 #if (NGX_BROKEN_SCM_RIGHTS)
     501 
     502     ch.command = 0;
     503 
     504 #else
     505 
     506     switch (signo) {
     507 
     508     case ngx_signal_value(NGX_SHUTDOWN_SIGNAL):
     509         ch.command = NGX_CMD_QUIT;
     510         break;
     511 
     512     case ngx_signal_value(NGX_TERMINATE_SIGNAL):
     513         ch.command = NGX_CMD_TERMINATE;
     514         break;
     515 
     516     case ngx_signal_value(NGX_REOPEN_SIGNAL):
     517         ch.command = NGX_CMD_REOPEN;
     518         break;
     519 
     520     default:
     521         ch.command = 0;
     522     }
     523 
     524 #endif
     525 
     526     ch.fd = -1;
     527 
     528 
     529     for (i = 0; i < ngx_last_process; i++) {
     530 
     531         ngx_log_debug7(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
     532                        "child: %d %P e:%d t:%d d:%d r:%d j:%d",
     533                        i,
     534                        ngx_processes[i].pid,
     535                        ngx_processes[i].exiting,
     536                        ngx_processes[i].exited,
     537                        ngx_processes[i].detached,
     538                        ngx_processes[i].respawn,
     539                        ngx_processes[i].just_spawn);
     540 
     541         if (ngx_processes[i].detached || ngx_processes[i].pid == -1) {
     542             continue;
     543         }
     544 
     545         if (ngx_processes[i].just_spawn) {
     546             ngx_processes[i].just_spawn = 0;
     547             continue;
     548         }
     549 
     550         if (ngx_processes[i].exiting
     551             && signo == ngx_signal_value(NGX_SHUTDOWN_SIGNAL))
     552         {
     553             continue;
     554         }
     555 
     556         if (ch.command) {
     557             if (ngx_write_channel(ngx_processes[i].channel[0],
     558                                   &ch, sizeof(ngx_channel_t), cycle->log)
     559                 == NGX_OK)
     560             {
     561                 if (signo != ngx_signal_value(NGX_REOPEN_SIGNAL)) {
     562                     ngx_processes[i].exiting = 1;
     563                 }
     564 
     565                 continue;
     566             }
     567         }
     568 
     569         ngx_log_debug2(NGX_LOG_DEBUG_CORE, cycle->log, 0,
     570                        "kill (%P, %d)" , ngx_processes[i].pid, signo);
     571 
     572         /* 发送信号给进程 */
     573         if (kill(ngx_processes[i].pid, signo) == -1) {
     574             err = ngx_errno;
     575             ngx_log_error(NGX_LOG_ALERT, cycle->log, err,
     576                           "kill(%P, %d) failed", ngx_processes[i].pid, signo);
     577 
     578             if (err == NGX_ESRCH) {
     579                 ngx_processes[i].exited = 1;
     580                 ngx_processes[i].exiting = 0;
     581                 ngx_reap = 1;
     582             }
     583 
     584             continue;
     585         }
     586 
     587         if (signo != ngx_signal_value(NGX_REOPEN_SIGNAL)) {
     588             ngx_processes[i].exiting = 1;
     589         }
     590     }
     591 }

      1. 该函数的 500-522 行都是根据接受到的信号 (signo) 设置ngx_channerl_t 结构体的 command 的值。该结构体是一个类似于管道的父子进程间进行通信的通道!

      2. 529-590(函数结束) 通过一个 for 循环遍历所有 worker 进程,然后根据每个进程所处的状态,给他们发送或不发送信号!例如. 程序的 541 行,如果遍历到的这个进程已经与父进程分离(detached变量为 1 ) 或者该进程的 pid=-1( 有问题的进程),则忽视这次操作,进入下一个循环。程序的 545 行、550 行都是进行相应的判断。程序的 556-567行 和 573-585 行都是将相应的命令通过对 channel 的操作 或者 kill 函数发送给子进程。

      从下一节开始我们将分析 ngx_start_worker_processes() 函数,该函数创建真正干活的 worker 进程! nginx 几乎所有的功能都是由 worker 进程来完成的!所以这个函数非常重要!

  • 相关阅读:
    Json对象与Json字符串互转(4种转换方式)
    Web.config配置文件详解
    jQuery BlockUI Plugin Demo 6(Options)
    jQuery BlockUI Plugin Demo 5(Simple Modal Dialog Example)
    jQuery BlockUI Plugin Demo 4(Element Blocking Examples)
    jQuery BlockUI Plugin Demo 3(Page Blocking Examples)
    jQuery BlockUI Plugin Demo 2
    <configSections> 位置引起的错误
    关于jQuery的cookies插件2.2.0版设置过期时间的说明
    jQuery插件—获取URL参数
  • 原文地址:https://www.cnblogs.com/zhuwbox/p/3983440.html
Copyright © 2011-2022 走看看