Nginx源代码阅读 第一部分- Nginx启动
上次
阅读Nginx源代码之一:前言。
修改
5411:5483d9e77b32 的含义是什么?
启动nginx
根据上次最后写的那样,从main函数开始自顶向下地阅读。因此要先找到main函数。
$ global -x main
main              202 src/core/nginx.c main(int argc, char *const *argv)
$
在这个主函数中,对各个组件(如定时器、正则表达式引擎、日志、信号等)进行了初始化。
int ngx_cdecl
main(int argc, char *const *argv)
{
            ・
            ・
    ngx_debug_init();
            ・
            ・
    ngx_time_init();
            ・
            ・
#if (NGX_PCRE)
    ngx_regex_init();
#endif
            ・
            ・
    log = ngx_log_init(ngx_prefix);
            ・ 
            ・
    if (ngx_init_signals(cycle->log) != NGX_OK) {
        return 1;
    }
            ・ 
            ・
同时,在ngx_init_cycle中进行端口的监听。
    cycle = ngx_init_cycle(&init_cycle);
这将在ngx_open_listening_sockets函数中执行。
    if (ngx_open_listening_sockets(cycle) != NGX_OK) {
        goto failed;
    }
除此之外,还将环境变量、命令行参数等信息保存在全局变量中。最后还有以下分支。
    if (ngx_process == NGX_PROCESS_SINGLE) {
        ngx_single_process_cycle(cycle);
    } else {
        ngx_master_process_cycle(cycle);
    }
通常情况下会调用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));
    if (sigprocmask(SIG_BLOCK, &set, NULL) == -1) {
        ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
                      "sigprocmask() failed");
    }
NGINX的主进程将在此之后启动工作进程,并在需要时启动缓存管理器进程,然后等待信号通过sigsuspend发送过来。有关如何通过信号控制NGINX的方法的介绍可以在官方网站上找到,详细信息请参阅此处。简单来说,它可以同时关闭NGINX的所有进程、实现平稳重启,并进行无停机版本升级等操作。
顺便提一下,Nginx会将进程名称分别设置为master、worker和cache-manager,然后进行相应的进程名称替换处理,这些处理在此附近完成。
    size = sizeof(master_process);
    for (i = 0; i < ngx_argc; i++) {
        size += ngx_strlen(ngx_argv[i]) + 1;
    }
    title = ngx_pnalloc(cycle->pool, size);
    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);
我们接下来来看一下启动worker进程的过程(在这个过程中还没有进入sigsuspend)。ngx_start_worker_processes是它的入口。
    ngx_start_worker_processes(cycle, ccf->worker_processes,
                               NGX_PROCESS_RESPAWN);
    ngx_start_cache_manager_processes(cycle, 0);
调用ngx_spawn_process的次数应与worker_processes的数量相等。
    for (i = 0; i < n; i++) {
        ngx_spawn_process(cycle, ngx_worker_process_cycle,
                          (void *) (intptr_t) i, "worker process", type);
                        ・
                        ・
    }
在这个函数中,通过使用ioctl或fcntl来进行套接字的各种设置后,从主进程fork出工作进程。
    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;
    }
当 master 进程在启动了所有的 worker 进程后,通过调用 sigsuspend 进入信号等待状态,此时 proc 将作为 ngx_spawn_process 函数的第二个参数 ngx_worker_process_cycle,成为 worker 进程的主循环。
所以,我读到了nginx已经启动并且单主多工作线程已经启动。这次就到这里了。
下次 (计划)
nginx源代码阅读之二:事件驱动引擎。