zoukankan      html  css  js  c++  java
  • nginx master 进程主流程

    nginx master 进程主流程

    之前有说到 nginx 进程模型-整体架构,下面来看一下 nginx master 进程的主要工作

    nginx 的入口 main 函数在 nginx.c 文件中

    函数原型为:

    int ngx_cdecl
    main(int argc, char *const *argv)
    

    在这个函数中,master 做了一系列的初始化操作

    最终在下面这个地方进入了主流程中:

    //...
        if (ngx_process == NGX_PROCESS_SINGLE) {
            ngx_single_process_cycle(cycle);
    
        } else {
            ngx_master_process_cycle(cycle);
        }
    //...
    

    因为我们主要看 master-worker 这种进程模型,所以进入 ngx_master_process_cycle

    设置信号屏蔽字,防止创建子进程过程中被信号中断

    // 先清空信号集
        sigemptyset(&set);
        sigaddset(&set, SIGCHLD);
        sigaddset(&set, SIGALRM);
        sigaddset(&set, SIGIO);
        sigaddset(&set, SIGINT);
        sigaddset(&set, ngx_signal_value(NGX_RECONFIGURE_SIGNAL));
        sigaddset(&set, ngx_signal_value(NGX_REOPEN_SIGNAL));
        sigaddset(&set, ngx_signal_value(NGX_NOACCEPT_SIGNAL));
        sigaddset(&set, ngx_signal_value(NGX_TERMINATE_SIGNAL));
        sigaddset(&set, ngx_signal_value(NGX_SHUTDOWN_SIGNAL));
        sigaddset(&set, ngx_signal_value(NGX_CHANGEBIN_SIGNAL));
    
    // 设置信号屏蔽字,将 set 中的信号设置为阻塞状态,防止创建worker 的过程中,被进来的信号打断
        if (sigprocmask(SIG_BLOCK, &set, NULL) == -1) {
            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
                          "sigprocmask() failed");
        }
    
    // 将 set 清空
        sigemptyset(&set);
    

    设置信号屏蔽字,防止在创建子进程的过程中被信号处理程序中断

    关于信号屏蔽字,引用 《UNIX 环境高级编程》 中信号一节的部分内容:

    进程可以选用“阻塞信号递送”。如果为进程产生了一个阻塞的信号,而且对该信号的动作是系统默认动作或捕捉该信号, 则为该进程将此信号保持为未决状态,直到该进程对此信号解除了阻塞, 或者将对此信号的动作更改为忽略。内核在递送一个原来被阻塞的信号给进程时(而不是在产生该信号时),才决定对它的处理方式。于是进程在信号递送给它之前仍可改变对该信号的动作。进程调用 sigpending 函数(见10.13节)来判定哪些信号是设置为阻塞并处于未决状态的。

    每个进程都有一个信号屏蔽字( signal mask),它规定了当前要阻塞递送到该进程的信号集。对于每种可能的信号,该屏蔽字中都有一位与之对应。对于某种信号,若其对应位已设置,则它当前是被阻塞的。进程可以调用 sigprocmask(在10.12节中说明)来检测和更改其当前信号屏蔽字。

    当然,在下面创建完子进程之后,会使用 sigsuspend 解除信号屏蔽,并使 master 进程进入休眠

    关于 sigsuspend 函数,简单来说,它是一个 sigprocmask(SIG_SETMASK, &emptyset, NULL)pause() 函数的结合体,不过相对于使用两个函数完成上述操作,sigsuspend原子操作

    具体的操作如下:

    1. 使用新的信号集合设置屏蔽字,在这里是清空屏蔽字
    2. 调用信号处理函数,并从信号处理程序返回
    3. 屏蔽字恢复成调用 sigsuspend 之前的值(再次不让进程被信号打断)

    设置 master 进程的 title

    static u_char  master_process[] = "master process";
    
        size = sizeof(master_process);
    
        for (i = 0; i < ngx_argc; i++) {
            size += ngx_strlen(ngx_argv[i]) + 1;
        }
    
        title = ngx_pnalloc(cycle->pool, size);
        if (title == NULL) {
            /* fatal */
            exit(2);
        }
    
        p = ngx_cpymem(title, master_process, sizeof(master_process) - 1);
        for (i = 0; i < ngx_argc; i++) {
            *p++ = ' ';
            p = ngx_cpystrn(p, (u_char *) ngx_argv[i], size);
        }
    
        ngx_setproctitle(title);
    

    将进程title设置成三个部分:

    1. 固定字符:nginx:
    2. 主进程标志: master process
    3. 命令行启动的命令,如:/home/ubuntu/mydisk/var/nginx-debug-1/sbin/nginx

    所以进程刚开始是这样显示的:

    $ ps aux | grep nginx | grep -v grep
    root      183117  0.0  0.0   4324  2680 ?        ts   17:07   0:00 /home/ubuntu/mydisk/var/nginx-debug-1/sbin/nginx
    

    设置了 title 之后,变成了下面这样:

    $ ps aux | grep nginx | grep -v grep
    root      183117  0.0  0.0   4324  2680 ?        ts   17:07   0:00 nginx: master process /home/ubuntu/mydisk/var/nginx-debug-1/sbin/nginx
    

    根据配置启动相应数量的 workercache 管理进程

    ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module);
    
    ngx_start_worker_processes(cycle, ccf->worker_processes,
                               NGX_PROCESS_RESPAWN);
    ngx_start_cache_manager_processes(cycle, 0);
    

    监听信号,并作出响应

    ngx_init_signals 函数中,对原始信号做了变量名映射,具体映射如下:

    信号 对应进程中的标志位变量 含义
    QUIT ngx_quit 优雅关闭服务
    TERM 或 INT ngx_terminate 强制关闭服务
    USR1 ngx_reopen 重新打开服务中的所有文件
    WINCH ngx_noaccept 所有子进程不再接受处理新的连接,实际相当于对所有的子进程发送 QUIT 信号
    USR2 ngx_change_binary 平滑升级到新版本的 Nginx 程序
    HUP ngx_reconfigure 重新读取配置文件并使服务对新配置项生效
    CHLD ngx_reap 有子进程意外结束
    master 会监控所有子进程,并在子进程意外退出时调用 ngx_reap_children 方法重启子进程

    master 并不是时刻不停的执行循环检测这些标志位,而是通过 sigsuspend 进入休眠,等待有信号唤醒进程时,再循环检测所有信号并处理。

    参考

    nginx中的ngx_cdecl

    Nginx源码|Nginx信号处理

    nginx 信号处理

    LINUX C中sigprocmask()函数用法

    linux信号的阻塞和未决

    《UNIX 环境高级编程》

  • 相关阅读:
    我们为什么要使用Spring Cloud?简介
    Spring注解@Component、@Repository、@Service、@Controller区别
    Spring boot 各种入门及问题
    spring boot与spring mvc的区别是什么?
    Android http Request / Response ContentType
    Android Studio添加aar
    Android 自定义线程池的实战
    Android AsyncTask 深度理解、简单封装、任务队列分析、自定义线程池
    EditText 显示明文和密码
    Android log 管理工具
  • 原文地址:https://www.cnblogs.com/wudanyang/p/14695028.html
Copyright © 2011-2022 走看看