前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >从nginx1.17.9源码理解nginx -s reload

从nginx1.17.9源码理解nginx -s reload

作者头像
theanarkh
发布2020-03-17 16:05:32
1.1K0
发布2020-03-17 16:05:32
举报
文章被收录于专栏:原创分享原创分享原创分享

使用nginx的时候,我们经常会使用nginx -s reload命令重启。下面我们就分析一下,执行这个命令的时候,nginx里发生了什么?我们从nginx的main函数开始。在main函数里,执行ngx_get_options函数命令行的初始化工作。 我们只看reload相关的逻辑。

// 解析命令行参数
static ngx_int_t
ngx_get_options(int argc, char *const *argv)
{
    u_char     *p;
    ngx_int_t   i;
    // 遍历每个参数./nginx -abc
    for (i = 1; i < argc; i++) {

        p = (u_char *) argv[i];
        // 命令行参数要以-开头
        if (*p++ != '-') {
            ngx_log_stderr(0, "invalid option: \"%s\"", argv[i]);
            return NGX_ERROR;
        }

        while (*p) {

            switch (*p++) {
             // ...
            // 给主进程发送一个信号
            case 's':
                // 紧跟随后(./nginx -sxxx),否则相隔了一个空格(./nginx -s xxx)
                if (*p) {
                    ngx_signal = (char *) p;

                } else if (argv[++i]) {
                    ngx_signal = argv[i];

                } else {
                    ngx_log_stderr(0, "option \"-s\" requires parameter");
                    return NGX_ERROR;
                }
                // 判断需要发送的信号
                if (ngx_strcmp(ngx_signal, "stop") == 0
                    || ngx_strcmp(ngx_signal, "quit") == 0
                    || ngx_strcmp(ngx_signal, "reopen") == 0
                    || ngx_strcmp(ngx_signal, "reload") == 0)
                {
                    ngx_process = NGX_PROCESS_SIGNALLER;
                    goto next;
                }

                ngx_log_stderr(0, "invalid option: \"-s %s\"", ngx_signal);
                return NGX_ERROR;
            }
        }

    next:

        continue;
    }

    return NGX_OK;
}

从上面的代码中我们知道,执行nginx -s reload的时候,nginx会设置ngx_signal 变量的值为reload。然后nginx在main函数里会判断这个标记。

     // 给主进程发送信号,则直接处理信号就行,不是启动nginx
    if (ngx_signal) {
        return ngx_signal_process(cycle, ngx_signal);
    }
ngx_int_t
ngx_signal_process(ngx_cycle_t *cycle, char *sig)
{
    ssize_t           n;
    ngx_pid_t         pid;
    ngx_file_t        file;
    ngx_core_conf_t  *ccf;
    u_char            buf[NGX_INT64_LEN + 2];

    // 从配置文件中拿到保存了主进程进程id的文件路径
    ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module);

    ngx_memzero(&file, sizeof(ngx_file_t));

    file.name = ccf->pid;
    file.log = cycle->log;
    // 打开保存了主进程进程id的文件
    file.fd = ngx_open_file(file.name.data, NGX_FILE_RDONLY,
                            NGX_FILE_OPEN, NGX_FILE_DEFAULT_ACCESS);

    if (file.fd == NGX_INVALID_FILE) {
        ngx_log_error(NGX_LOG_ERR, cycle->log, ngx_errno,
                      ngx_open_file_n " \"%s\" failed", file.name.data);
        return 1;
    }
    // 把进程id读进来
    n = ngx_read_file(&file, buf, NGX_INT64_LEN + 2, 0);

    if (ngx_close_file(file.fd) == NGX_FILE_ERROR) {
        ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
                      ngx_close_file_n " \"%s\" failed", file.name.data);
    }

    while (n-- && (buf[n] == CR || buf[n] == LF)) { /* void */ }
    // 解析字符串为int型
    pid = ngx_atoi(buf, ++n);

    // 处理
    return ngx_os_signal_process(cycle, sig, pid);

}

这时候nginx拿到了主进程进程id。

ngx_int_t
ngx_os_signal_process(ngx_cycle_t *cycle, char *name, ngx_pid_t pid)
{
    ngx_signal_t  *sig;

    for (sig = signals; sig->signo != 0; sig++) {
        // 找到给主进程发的信号
        if (ngx_strcmp(name, sig->name) == 0) {
            // 给主进程进程发信号
            if (kill(pid, sig->signo) != -1) {
                return 0;
            }
        }
    }

    return 1;
}

nginx会从signals变量中找到reload信号的配置。

 { 
         ngx_signal_value(NGX_RECONFIGURE_SIGNAL),
      "SIG" ngx_value(NGX_RECONFIGURE_SIGNAL),
      "reload",
      ngx_signal_handler 
 },

然后给主进程发送SIGNGX_RECONFIGURE_SIGNAL(linux的HUP)信号。就完成了使命。我们来看看主进程关于信号处理这块都做了些什么。nginx在启动的时候会注册信号处理函数。

ngx_signal_t  signals[] = {
    { 
      ngx_signal_value(NGX_RECONFIGURE_SIGNAL),
      "SIG" ngx_value(NGX_RECONFIGURE_SIGNAL),
      "reload",
      ngx_signal_handler 
    },
    ...
}
// 根据signals变量定义的信号信息,注册到系统
ngx_int_t ngx_init_signals(ngx_log_t *log)
{
    ngx_signal_t      *sig;
    struct sigaction   sa;

    for (sig = signals; sig->signo != 0; sig++) {
        ngx_memzero(&sa, sizeof(struct sigaction));

        if (sig->handler) {
            sa.sa_sigaction = sig->handler;
            sa.sa_flags = SA_SIGINFO;

        } else {
            sa.sa_handler = SIG_IGN;
        }
        // 信号处理函数在执行的时候,不需要屏蔽其他信号
        sigemptyset(&sa.sa_mask);
        if (sigaction(sig->signo, &sa, NULL) == -1) {

        }
    }

    return NGX_OK;
}

所以我们知道reload的时候,主进程收到信号后,会执行ngx_signal_handler函数。该函数有这么一段代码。

 case ngx_signal_value(NGX_RECONFIGURE_SIGNAL):
            ngx_reconfigure = 1;
            action = ", reconfiguring";
            break;

主要是设置了ngx_reconfigure = 1;我们知道,nginx启动后,主进程是在死循环里监控worker进程的工作和处理信号的。所以我们看一下死循环里面的代码。

  if (ngx_reconfigure) {
            ngx_reconfigure = 0;

            cycle = ngx_init_cycle(cycle);

            ngx_cycle = cycle;
            ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx,
                                                   ngx_core_module);
            // 重新fork work进程和cache进程
            ngx_start_worker_processes(cycle, ccf->worker_processes,
                                       NGX_PROCESS_JUST_RESPAWN);
            ngx_start_cache_manager_processes(cycle, 1);

            ngx_msleep(100);

            live = 1;
            // 杀死旧的worker进程
            ngx_signal_worker_processes(cycle,
                                        ngx_signal_value(NGX_SHUTDOWN_SIGNAL));
        }

主进程首先启动新的一批worker然后杀死旧的worker。最后完成重启。具体的逻辑比较细,后续再分析。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-03-15,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 编程杂技 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档