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源代码阅读之二:事件驱动引擎。