zoukankan      html  css  js  c++  java
  • Nginx之监控进程和工作进程

    1. 函数调用分析

    在开启 master 的情况下,多进程模型的下的入口函数为 ngx_master_process_cycle,如下:

    int mian()
    {
    ...
        if (ngx_process == NGX_PROCESS_SINGLE)
        {
            /* 单进程模型下的入口函数 */
            ngx_single_process_cycle(cycle);
        }
        else
        {
            /* 多进程模型下的入口函数 */
            ngx_master_process_cycle(cycle);
        }
        
        return 0;
    }
    

    Nginx 核心进程模型框图

    1.1 ngx_master_process_cycle:

    static ngx_cycle_t      ngx_exit_cycle;
    static ngx_log_t        ngx_exit_log;
    static ngx_open_file_t  ngx_exit_log_file;
    
    /*
     * 参数意义:
     * - cycle是当前进程ngx_cycle_t的结构体指针
     * 
     * 执行意义:
     * 进入master进程的工作循环
     */
    void ngx_master_process_cycle(ngx_cycle_t *cycle)
    {
        char              *title;
        u_char            *p;
        size_t             size;
        ngx_int_t          i;
        ngx_uint_t         n, sigio;
        sigset_t           set;
        struct itimerval   itv;
        ngx_uint_t         live;
        ngx_msec_t         delay;
        ngx_listening_t   *ls;
        ngx_core_conf_t   *ccf;
    
        /* 将下列信号添加到 set 信号集中 */
        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));
    
        /*
         * sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
         * 函数既可以修改进程的信号掩码,又可获取现有掩码,或者两者皆可。
         * - SIG_BLOCK:将set指向信号集内的指定信号添加到信号掩码中。换言之,将信号掩码
         *   设置为其当前值和set的并集。
         * - SIG_UNBLOCK:将set指向信号集中的指定信号从信号掩码中移除。即使要接触阻塞的信号当前
         *   并未处于阻塞状态,也不会返回错误。
         * - SIG_SETMASK:将set指向的信号集赋给信号掩码。
         */
         
        /* 临时阻塞上面所示信号,防止其信号处理器将某些关键代码片段的执行中断,然后下面调用
         * sigsuspend()解除对信号的阻塞,然后暂停执行,直至有信号到达. */
        if (sigprocmask(SIG_BLOCK, &set, NULL) == -1) {
            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
                          "sigprocmask() failed");
        }
    
        sigemptyset(&set);
    
        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);
    
        ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module);
    
        /* 该函数用于fork()产生子进程,该函数的主体是一个无限for( ;; )循环,持续不断地处理
         * 客户端的服务请求,而主进程继续执行ngx_master_process_cycle()函数,也就是作为监控
         * 进程执行主体for(;;)循环,这也是一个无限循环,直到进程终止才退出. */
        ngx_start_worker_processes(cycle, ccf->worker_processes,
                                   NGX_PROCESS_RESPAWN);
        ngx_start_cache_manager_processes(cycle, 0);
    
        ngx_new_binary = 0;
        delay = 0;
        sigio = 0;
        live = 1;
    
        for ( ;; ) {
            if (delay) {
                if (ngx_sigalrm) {
                    sigio = 0;
                    delay *= 2;
                    ngx_sigalrm = 0;
                }
    
                ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
                               "termination cycle: %M", delay);
    
                itv.it_interval.tv_sec = 0;
                itv.it_interval.tv_usec = 0;
                itv.it_value.tv_sec = delay / 1000;
                itv.it_value.tv_usec = (delay % 1000 ) * 1000;
    
                if (setitimer(ITIMER_REAL, &itv, NULL) == -1) {
                    ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
                                  "setitimer() failed");
                }
            }
    
            ngx_log_debug0(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "sigsuspend");
    
            /*
             * sigsuspend(const sigset_t *mask);
             * 该函数将以mask所指向的信号集来替换进程的信号掩码,然后挂起进程的执行,
             * 直到其捕获到信号,并从信号处理器中返回。一旦处理器返回,sigsuspend()
             * 会将进程信号掩码恢复为调用前的值。
             */
            
            /* 该函数使得监控进程的大部分时间都处于挂起等待状态,直到监控进程接收到信号为止。
             * 当监控进程接收到信号时,信号处理函数ngx_signal_handler()就会被执行。*/
            sigsuspend(&set);
    
            ngx_time_update();
    
            ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
                           "wake up, sigio %i", sigio);
    
            /* 有子进程退出? */
            if (ngx_reap) {
                ngx_reap = 0;
                ngx_log_debug0(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "reap children");
    
                live = ngx_reap_children(cycle);
            }
    
            /* 进程要退出或终止? */
            if (!live && (ngx_terminate || ngx_quit)) {
                ngx_master_process_exit(cycle);
            }
    
            /* 进程要终止? 
             * 结束比较粗暴,不过它通过使用SIGKILL信号能保证在一段
             * 时间后必定被结束掉. */
            if (ngx_terminate) {
                if (delay == 0) {
                    delay = 50;
                }
    
                if (sigio) {
                    sigio--;
                    continue;
                }
    
                sigio = ccf->worker_processes + 2 /* cache processes */;
    
                if (delay > 1000) {
                    ngx_signal_worker_processes(cycle, SIGKILL);
                } else {
                    ngx_signal_worker_processes(cycle,
                                           ngx_signal_value(NGX_TERMINATE_SIGNAL));
                }
    
                continue;
            }
    
            /* 进程要退出? 
             * 该结束比较优雅,会让Nginx监控进程做一些清理工作且等待
             * 子进程也完全清理并退出之后才终止. */
            if (ngx_quit) {
                ngx_signal_worker_processes(cycle,
                                            ngx_signal_value(NGX_SHUTDOWN_SIGNAL));
    
                ls = cycle->listening.elts;
                for (n = 0; n < cycle->listening.nelts; n++) {
                    if (ngx_close_socket(ls[n].fd) == -1) {
                        ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_socket_errno,
                                      ngx_close_socket_n " %V failed",
                                      &ls[n].addr_text);
                    }
                }
                cycle->listening.nelts = 0;
    
                continue;
            }
    
            /* 重新加载配置? */
            if (ngx_reconfigure) {
                ngx_reconfigure = 0;
    
                if (ngx_new_binary) {
                    ngx_start_worker_processes(cycle, ccf->worker_processes,
                                               NGX_PROCESS_RESPAWN);
                    ngx_start_cache_manager_processes(cycle, 0);
                    ngx_noaccepting = 0;
    
                    continue;
                }
    
                ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "reconfiguring");
    
                cycle = ngx_init_cycle(cycle);
                if (cycle == NULL) {
                    cycle = (ngx_cycle_t *) ngx_cycle;
                    continue;
                }
    
                ngx_cycle = cycle;
                ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx,
                                                       ngx_core_module);
                ngx_start_worker_processes(cycle, ccf->worker_processes,
                                           NGX_PROCESS_JUST_RESPAWN);
                ngx_start_cache_manager_processes(cycle, 1);
    
                /* allow new processes to start */
                ngx_msleep(100);
    
                live = 1;
                ngx_signal_worker_processes(cycle,
                                            ngx_signal_value(NGX_SHUTDOWN_SIGNAL));
            }
    
            if (ngx_restart) {
                ngx_restart = 0;
                ngx_start_worker_processes(cycle, ccf->worker_processes,
                                           NGX_PROCESS_RESPAWN);
                ngx_start_cache_manager_processes(cycle, 0);
                live = 1;
            }
    
            if (ngx_reopen) {
                ngx_reopen = 0;
                ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "reopening logs");
                ngx_reopen_files(cycle, ccf->user);
                ngx_signal_worker_processes(cycle,
                                            ngx_signal_value(NGX_REOPEN_SIGNAL));
            }
    
            if (ngx_change_binary) {
                ngx_change_binary = 0;
                ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "changing binary");
                ngx_new_binary = ngx_exec_new_binary(cycle, ngx_argv);
            }
    
            if (ngx_noaccept) {
                ngx_noaccept = 0;
                ngx_noaccepting = 1;
                ngx_signal_worker_processes(cycle,
                                            ngx_signal_value(NGX_SHUTDOWN_SIGNAL));
            }
        }
    }
    

    1.2 ngx_start_worker_processes

    /*
     * 参数含义:
     * - cycle: 是当前进程的ngx_cycle_t结构体指针
     * - n: 是启动子进程的个数
     * - type: 是启动方式,取值范围有以下5个:
     *      - NGX_PROCESS_RESPAWN
     *      - NGX_PROCESS_NORESPAWN
     *      - NGX_PROCESS_JUST_SPAWN
     *      - NGX_PROCESS_JUST_RESPAWN
     *      - NGX_PROCESS_DETACHED
     *   type的值将会影响ngx_process_t结构体的respawn、detached、just_spawn标志位的值.
     *
     * 执行意义:
     * 启动n个worker子进程,并设置好每个子进程与master父进程之间使用socketpair系统调用
     * 建立起来的socket句柄通信机制.
     */
    static void ngx_start_worker_processes(ngx_cycle_t *cycle, ngx_int_t n, ngx_int_t type)
    {
        ngx_int_t      i;
        ngx_channel_t  ch;
    
        ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "start worker processes");
    
        ngx_memzero(&ch, sizeof(ngx_channel_t));
    
        ch.command = NGX_CMD_OPEN_CHANNEL;
    
        for (i = 0; i < n; i++) {
    
            ngx_spawn_process(cycle, ngx_worker_process_cycle,
                              (void *) (intptr_t) i, "worker process", type);
    
            ch.pid = ngx_processes[ngx_process_slot].pid;       // 子进程的id号
            ch.slot = ngx_process_slot; // 子进程的相关信息在全局数组ngx_processes中的下标
            // 父进程使用的socket描述符
            ch.fd = ngx_processes[ngx_process_slot].channel[0];
    
            /* 父进程fork()生成一个新子进程后,就会立即调用该函数ngx_pass_open_channel()
             * 把这个子进程的相关信息告知给其前面已生成的子进程. */
            ngx_pass_open_channel(cycle, &ch);
        }
    }
    

    1.3 ngx_spawn_process

    ngx_pid_t ngx_spawn_process(ngx_cycle_t *cycle, ngx_spawn_proc_pt proc, void *data, 
                                    char *name, ngx_int_t respawn)
    {
        u_long     on;
        ngx_pid_t  pid;
        ngx_int_t  s;
    
        if (respawn >= 0)
        {
            s = respawn;
        }
        else
        {
            /* 遍历所有存活的子进程,找到一个空闲的下标值 */
            for (s = 0; s < ngx_last_process; s++)
            {
                if (ngx_processes[s].pid == -1)
                {
                    break;
                }
            }
    
            if (s == NGX_MAX_PROCESSES)
            {
                ngx_log_error(NGX_LOG_ALERT, cycle->log, 0, 
                              "no more than %d processes can be spawned", 
                              NGX_MAX_PROCESSES);
                return NGX_INVALID_PID;
            }
        }
    
    
        if (respawn != NGX_PROCESS_DETACHED)
        {
            /* Solaris 9 still has no AF_LOCAL */
    
            /* 采用socketpair()函数创造一对未命名的UNIX域套接字来进行Linux下具有
             * 亲缘关系的进程之间的双向通信. */
    
            /* 
             * 在fork()之前,先调用了socketpair()创建了一对socket描述符存放在变量
             * ngx_processes[s].channel内(其中s标志在ngx_processes数组内第一个可用
             * 元素的下标,比如最开始产生第一个工作进程时,可用元素的下标s为0),而
             * 在fork()之后,由于子进程继承了父进程的资源,那么父子进程就都有了这一对
             * socket描述符,而Nginx将channel[0]给父进程使用,channel[1]给子进程使用,
             * 这样分别错开地使用不同socket描述符,即可以实现父子进程之间的双向通信.
             */
    
            if (socketpair(AF_UNIX, SOCK_STREAM, 0, ngx_processes[s].channel) == -1)
            {
                ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, 
                              "socketpair() failed while spawning "%s"", name);
                return NGX_INVALID_PID;
            }
    
            ngx_log_debug2(NGX_LOG_DEBUG_CORE, cycle->log, 0, 
                           "channel: master socket:%d, worker socket:%d", 
                           ngx_processes[s].channel[0],
                           ngx_processes[s].channel[1]);
    
            if (ngx_nonblocking(ngx_processes[s].channel[0]) == -1)
            {
                ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, 
                              ngx_nonblocking_n " failed while spawning "%s"", 
                              name);
                ngx_close_channel(ngx_processes[s].channel, cycle->log);
                return NGX_INVALID_PID;
            }
    
            if (ngx_nonblocking(ngx_processes[s].channel[1]) == -1)
            {
                ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, 
                              ngx_nonblocking_n " failed while spawning "%s"", 
                              name);
                ngx_close_channel(ngx_processes[s].channel, cycle->log);
                return NGX_INVALID_PID;
            }
    
            on = 1;
            if (ioctl(ngx_processes[s].channel[0], FIOASYNC, &on) == -1)
            {
                ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, 
                              "ioctl(FIOASYNC) failed while spawning "%s"", name);
                ngx_close_channel(ngx_processes[s].channel, cycle->log);
                return NGX_INVALID_PID;
            }
    
            
            if (fcntl(ngx_processes[s].channel[0], F_SETOWN, ngx_pid) == -1)
            {
                ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, 
                              "fcntl(F_SETOWN) failed while spawning "%s"", name);
                ngx_close_channel(ngx_processes[s].channel, cycle->log);
                return NGX_INVALID_PID;
            }
    
            if (fcntl(ngx_processes[s].channel[0], F_SETFD, FD_CLOEXEC) == -1)
            {
                ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, 
                              "fcntl(FD_CLOEXEC) failed while spawning "%s"", name);
                ngx_close_channel(ngx_processes[s].channel, cycle->log);
                return NGX_INVALID_PID;
            }
    
            if (fcntl(ngx_processes[s].channel[1], F_SETFD, FD_CLOEXEC) == -1)
            {
                ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, 
                              "fcntl(FD_CLOEXEC) failed while spawning "%s"", name);
                ngx_close_channel(ngx_processes[s].channel, cycle->log);
                return NGX_INVALID_PID;
            }
    
            /* 记录子进程使用的socket描述符 */
            ngx_channel = ngx_processes[s].channel[1];
        }
        else
        {
            ngx_processes[s].channel[0] = -1;
            ngx_processes[s].channel[1] = -1;
        }
    
        /* 记录该子进程的相关信息在全局数组中的下标 */
        ngx_process_slot = s;
    
        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(); // 获取该子进程的进程id号
            proc(cycle, data);
            break;
    
        default:
            break;
        }
    
        ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "start child %s pid:%P", name, pid);
    
        ngx_processes[s].pid = pid;  // 记录子进程的pid
        ngx_processes[s].exited = 0;
    
        if (respawn >= 0)
        {
            return pid;
        }
    
        ngx_processes[s].proc = proc;
        ngx_processes[s].data = data;
        ngx_processes[s].name = name;
        ngx_processes[s].exiting = 0;
    
        switch (respawn)
        {
        case NGX_PROCESS_NORESPAWN:
            ngx_processes[s].respawn = 0;
            ngx_processes[s].just_spawn = 0;
            ngx_processes[s].detached = 0;
            break;
    
        case NGX_PROCESS_JUST_SPAWN:
            ngx_processes[s].respawn = 0;
            ngx_processes[s].just_spawn = 1;
            ngx_processes[s].detached = 0;
            break;
    
        case NGX_PROCESS_RESPAWN:
            ngx_processes[s].respawn = 1;
            ngx_processes[s].just_spawn = 0;
            ngx_processes[s].detached = 0;
            break;
    
        case NGX_PROCESS_JUST_RESPAWN:
            ngx_processes[s].respawn = 1;
            ngx_processes[s].just_spawn = 1;
            ngx_processes[s].detached = 0;
            break;
    
        case NGX_PROCESS_DETACHED:
            ngx_processes[s].respawn = 0;
            ngx_processes[s].just_spawn = 0;
            ngx_processes[s].detached = 1;
            break;
        }
    
        if (s == ngx_last_process)
        {
            ngx_last_process++;
        }
    
        return pid;
    }
    

    1.4 ngx_worker_process_cycle

    /*
     * 参数意义:
     * - cycle是当前进程的ngx_cycle_t结构体指针,这里还未开始使用data参数,所以data一般为NULL
     * 
     * 执行意义:
     * 进入worker进程工作的循环
     */
    static void ngx_worker_process_cycle(ngx_cycle_t *cycle, void *data)
    {
        ngx_int_t worker = (intptr_t) data;
    
        ngx_process = NGX_PROCESS_WORKER;
        ngx_worker = worker;
    
        /* 子进程的启动初始化函数 */
        ngx_worker_process_init(cycle, worker);
    
        /* 设置子进程的进程名 */
        ngx_setproctitle("worker process");
    
        for ( ;; ) {
    
            if (ngx_exiting) {
                if (ngx_event_no_timers_left() == NGX_OK) {
                    ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "exiting");
                    ngx_worker_process_exit(cycle);
                }
            }
    
            ngx_log_debug0(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "worker cycle");
    
            /* 最重要的函数:监听并处理事件 */
            ngx_process_events_and_timers(cycle);
    
            if (ngx_terminate) {
                ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "exiting");
                ngx_worker_process_exit(cycle);
            }
    
            if (ngx_quit) {
                ngx_quit = 0;
                ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0,
                              "gracefully shutting down");
                ngx_setproctitle("worker process is shutting down");
    
                if (!ngx_exiting) {
                    ngx_exiting = 1;
                    ngx_set_shutdown_timer(cycle);
                    ngx_close_listening_sockets(cycle);
                    ngx_close_idle_connections(cycle);
                }
            }
    
            if (ngx_reopen) {
                ngx_reopen = 0;
                ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "reopening logs");
                ngx_reopen_files(cycle, -1);
            }
        }
    }
    

    1.5 ngx_event_accept

    每个监听待连接事件的回调函数都是 ngx_event_accept,一旦监听到客户端发来的连接请求,就会调用该回调方法。

    致此,就完成了一个连接的建立.

  • 相关阅读:
    Java实现 LeetCode 400 第N个数字
    Java实现 LeetCode 400 第N个数字
    Java实现 LeetCode 399 除法求值
    Java实现 LeetCode 399 除法求值
    Java实现 LeetCode 399 除法求值
    Java实现 LeetCode 398 随机数索引
    Java实现 LeetCode 398 随机数索引
    Java实现 LeetCode 398 随机数索引
    linux中的cd ..和cd -命令有什么区别?
    GCC使用
  • 原文地址:https://www.cnblogs.com/jimodetiantang/p/8971311.html
Copyright © 2011-2022 走看看