nginx继承socket 和 热代码替换

来源:互联网 发布:郑州轻工业网络教学 编辑:程序博客网 时间:2024/05/21 07:55
    在看nginx源码的时候,遇到这样一个函数:ngx_add_inherited_sockets, 这个函数在main函数里被调用。
  里面具体做的事情就是:从一个环境变量读出socket文件描述符,然后这个socket文件描述符就可以用了,不用调用socket()函数创建。具体可以看这位大神的博客:http://blog.csdn.net/livelylittlefish/article/details/7277607
 
  但我读完这篇博客还是有一个困惑的问题,可能是我智商捉急不能理解大神的文章:为什么socket从环境变量里读出来,就能用了?
 
    进过反复阅读代码终于懂了,这就是nginx热启动。
    
    来看一下这个热启动的执行过程。
    
    1:给nginx发一个USR2信号:在ngx_process.c文件里有一个signals信号数组,这个数组定义了nginx所支持的信号,
    
    其中一个,
    { ngx_signal_value(NGX_CHANGEBIN_SIGNAL),
      "SIG" ngx_value(NGX_CHANGEBIN_SIGNAL),
      "",
      ngx_signal_handler },
    
    ngx_config.h里面有
    #define NGX_CHANGEBIN_SIGNAL     USR2
    
    2:处理函数ngx_signal_hanler做了啥:
      ngx_process.c:
      
      case ngx_signal_value(NGX_CHANGEBIN_SIGNAL):
            if (getppid() > 1 || ngx_new_binary > 0) {

                /*
                 * Ignore the signal in the new binary if its parent is
                 * not the init process, i.e. the old binary's process
                 * is still running.  Or ignore the signal in the old binary's
                 * process if the new binary's process is already running.
                 */
                /* 这里给出了详细的注释,更通俗一点来讲,就是说,进程现在是一个
                * master(新的master进程),但是当他的父进程old master还在运行的话,
                * 这时收到了USR2信号,我们就忽略它,不然就成了新master里又要生成
                * master。。。另外一种情况就是,old master已经开始了生成新master的过程
                * 中,这时如果又有USR2信号到来,那么也要忽略掉。。。(不知道够不够通俗=.=)
                参考文档:http://blog.csdn.net/dingyujie/article/details/7192144
                */
                action = ", ignoring";
                ignore = 1;
                break;
            }

            //正常情况下,需要热代码替换,设置标志位
            ngx_change_binary = 1;
            action = ", changing binary";
            break;
            
      
       里面设置了ngx_change_binary = 1,这一句是设置热启动标志
       
    3:ngx_process_cycle.c里面有个 ngx_master_process_cycle 这里面会循环检测各种标志,其中就包括热启动
    
    //热代码替换
    if (ngx_change_binary) { // 信号处理函数里将这个设置成了1
      ngx_change_binary = 0;
      ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "changing binary");
      //进行热代码替换,这里是调用execve来执行新的代码
      ngx_new_binary = ngx_exec_new_binary(cycle, ngx_argv);
    }
    
    4:ngx_exec_new_binary干了啥?
      
        1:现将socket写到环境变量里面
        2:pid = ngx_execute(cycle, &ctx)
        
    5:ngx_execute干了啥?
    
      ngx_pid_t
      ngx_execute(ngx_cycle_t *cycle, ngx_exec_ctx_t *ctx)
      {
        return ngx_spawn_process(cycle, ngx_execute_proc, ctx, ctx->name,
                             NGX_PROCESS_DETACHED);
      }
    6: ngx_spawn_process干了啥?
      ngx_spawn_process(ngx_cycle_t *cycle, ngx_spawn_proc_pt proc, void *data,
    char *name, ngx_int_t respawn);
      这个函数创建子进程,并让子进程执行proc函数,第五步传进来的是ngx_execute_proc
      
      ngx_spawn_process函数里有://创建子进程
        pid = fork();

        switch (pid) {

        case -1:
          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 0:
          ngx_pid = ngx_getpid();
          //在子进程中执行传递进来的函数,即工作进程的具体工作
          proc(cycle, data);
          break;

        default:
          break;
      }
     7:ngx_execute_proc做了啥:
       static void
       ngx_execute_proc(ngx_cycle_t *cycle, void *data)
       {
         ngx_exec_ctx_t  *ctx = data;

         if (execve(ctx->path, ctx->argv, ctx->envp) == -1) {
          ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
                      "execve() failed while executing %s \"%s\"",
                      ctx->name, ctx->path);
       }

       exit(1);
       }
       
       这个函数里调用的execve
       
       而文件描述符只要不设置close_on_exec就可以被继承下来。
       
       说到这里,我感觉原来的我已经懂了。
0 0