Nginx源码分析:启动流程

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_33339479/article/details/89154354

nginx源码分析

nginx-1.11.1
参考书籍《深入理解nginx模块开发与架构解析》

nginx简介

Nginx的作为服务端软件,表现的主要特点是更快、高扩展、高可靠性、低内存消耗、单机支持10万以上的并发连接和热部署等优点。Nginx是一般运行模型master-worker,启动多个worker来处理请求,一般master进程不会对用户提供服务,只用于管理真正提供服务的wroker进程,主要处理命令行服务,包括如启动、停止、平滑升级和重载配置文件等工作,当任意一个worker出现意外退出时,master进程会立刻启动新的worker进程继续提供服务,保证提供服务的worker数量与配置的相同。

在这里插入图片描述

nginx启动流程概述

相关的Nginx的配置文档大家可以自行查看,现在我们主要分析一下nginx的代码是如何启动并执行的,直接查看nginx.c中的main函数。

int ngx_cdecl
main(int argc, char *const *argv)
{
    ngx_buf_t        *b;
    ngx_log_t        *log;
    ngx_uint_t        i;
    ngx_cycle_t      *cycle, init_cycle;
    ngx_conf_dump_t  *cd;
    ngx_core_conf_t  *ccf;

    ngx_debug_init();                                   // 检查是否设置为debug模式

    if (ngx_strerror_init() != NGX_OK) {                // 初始化错误类型数组
        return 1;
    }

    if (ngx_get_options(argc, argv) != NGX_OK) {        // 获取解析的输入参数
        return 1;
    }

    if (ngx_show_version) {                             // 是否是显示输出nginx版本号
        ngx_show_version_info();                        // 显示输出版本号

        if (!ngx_test_config) {
            return 0;
        }
    }

    /* TODO */ ngx_max_sockets = -1;

    ngx_time_init();                                    // 时间初始化

#if (NGX_PCRE)
    ngx_regex_init();                                   // 正则初始化
#endif

    ngx_pid = ngx_getpid();                             // 获取当前的pid

    log = ngx_log_init(ngx_prefix);                     // 初始化日志
    if (log == NULL) {
        return 1;
    }

    /* STUB */
#if (NGX_OPENSSL)
    ngx_ssl_init(log);                                  // ssl初始化
#endif

    /*
     * init_cycle->log is required for signal handlers and
     * ngx_process_options()
     */

    ngx_memzero(&init_cycle, sizeof(ngx_cycle_t));          // 初始化init_cycle
    init_cycle.log = log;                                   // 设置日志
    ngx_cycle = &init_cycle;                                // 赋值

    init_cycle.pool = ngx_create_pool(1024, log);           // 创建1024个池,保证工作进程
    if (init_cycle.pool == NULL) {
        return 1;
    }

    if (ngx_save_argv(&init_cycle, argc, argv) != NGX_OK) {         // 保存命令行中输入的参数
        return 1;
    }

    if (ngx_process_options(&init_cycle) != NGX_OK) {               // 将ngx_get_options中获得参数赋值到ngx_cycle中
        return 1;
    }

    if (ngx_os_init(log) != NGX_OK) {                               // 初始化系统相关变量 如cpu profile等信息
        return 1; 
    }

    /*
     * ngx_crc32_table_init() requires ngx_cacheline_size set in ngx_os_init()
     */

    if (ngx_crc32_table_init() != NGX_OK) {                         // 初始化hash表
        return 1;
    }

    if (ngx_add_inherited_sockets(&init_cycle) != NGX_OK) {         // 继承socket套接字,在平衡启动的时候继承
        return 1;
    }

    if (ngx_preinit_modules() != NGX_OK) {                          // 初始化模块并编号
        return 1;
    }

    cycle = ngx_init_cycle(&init_cycle);                            // 初始化全局cycle
    if (cycle == NULL) {
        if (ngx_test_config) {
            ngx_log_stderr(0, "configuration file %s test failed",
                           init_cycle.conf_file.data);
        }

        return 1;
    }

    if (ngx_test_config) {
        if (!ngx_quiet_mode) {
            ngx_log_stderr(0, "configuration file %s test is successful",
                           cycle->conf_file.data);
        }

        if (ngx_dump_config) {
            cd = cycle->config_dump.elts;

            for (i = 0; i < cycle->config_dump.nelts; i++) {

                ngx_write_stdout("# configuration file ");
                (void) ngx_write_fd(ngx_stdout, cd[i].name.data,
                                    cd[i].name.len);
                ngx_write_stdout(":" NGX_LINEFEED);

                b = cd[i].buffer;

                (void) ngx_write_fd(ngx_stdout, b->pos, b->last - b->pos);
                ngx_write_stdout(NGX_LINEFEED);
            }
        }

        return 0;
    }

    if (ngx_signal) {
        return ngx_signal_process(cycle, ngx_signal);               // 如果有信号则进行信号处理
    }

    ngx_os_status(cycle->log);                                      

    ngx_cycle = cycle;

    ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module);  // 获取配置

    if (ccf->master && ngx_process == NGX_PROCESS_SINGLE) {                     // 检查是否是单个进程启动
        ngx_process = NGX_PROCESS_MASTER;
    }

#if !(NGX_WIN32)

    if (ngx_init_signals(cycle->log) != NGX_OK) {
        return 1;
    }

    if (!ngx_inherited && ccf->daemon) {
        if (ngx_daemon(cycle->log) != NGX_OK) {
            return 1;
        }

        ngx_daemonized = 1;
    }

    if (ngx_inherited) {
        ngx_daemonized = 1;
    }

#endif

    if (ngx_create_pidfile(&ccf->pid, cycle->log) != NGX_OK) {              // 创建pid 文件
        return 1;
    }

    if (ngx_log_redirect_stderr(cycle) != NGX_OK) {                         // 重定向日志输出
        return 1;
    }

    if (log->file->fd != ngx_stderr) {
        if (ngx_close_file(log->file->fd) == NGX_FILE_ERROR) {
            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
                          ngx_close_file_n " built-in log failed");
        }
    }

    ngx_use_stderr = 0;

    if (ngx_process == NGX_PROCESS_SINGLE) {                                // 是否是单个主进程启动
        ngx_single_process_cycle(cycle);

    } else {
        ngx_master_process_cycle(cycle);                                    // 一个master 多个worker启动
    }

    return 0;
}

其中几个相对重要的函数分析一下。

传入参数ngx_get_options解析

ngx_get_options函数主要是解析传入的参数值。

static ngx_int_t
ngx_get_options(int argc, char *const *argv)
{
    u_char     *p;
    ngx_int_t   i;

    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 '?':
            case 'h':
                ngx_show_version = 1;
                ngx_show_help = 1;
                break;                                                  // 如果是h则显示版本信息并显示帮助信息

            case 'v':
                ngx_show_version = 1;                                   // 显示版本信息  
                break;

            case 'V':
                ngx_show_version = 1;                                   // 显示版本信息并显示配置信息
                ngx_show_configure = 1;
                break;

            case 't':
                ngx_test_config = 1;                                    // 测试
                break;

            case 'T':
                ngx_test_config = 1;
                ngx_dump_config = 1;
                break;

            case 'q':
                ngx_quiet_mode = 1;                                     // 不显示error以下内容
                break;

            case 'p':
                if (*p) {
                    ngx_prefix = p;
                    goto next;
                }

                if (argv[++i]) {
                    ngx_prefix = (u_char *) argv[i];
                    goto next;
                }

                ngx_log_stderr(0, "option \"-p\" requires directory name");
                return NGX_ERROR;

            case 'c':
                if (*p) {                                                       // 指定配置文件
                    ngx_conf_file = p;
                    goto next;
                }

                if (argv[++i]) {
                    ngx_conf_file = (u_char *) argv[i];
                    goto next;
                }

                ngx_log_stderr(0, "option \"-c\" requires file name");
                return NGX_ERROR;

            case 'g':
                if (*p) {                                                       // 指定全局配置项
                    ngx_conf_params = p;
                    goto next;
                }

                if (argv[++i]) {
                    ngx_conf_params = (u_char *) argv[i];
                    goto next;
                }

                ngx_log_stderr(0, "option \"-g\" requires parameter");
                return NGX_ERROR;

            case 's': 
                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;

            default:
                ngx_log_stderr(0, "invalid option: \"%c\"", *(p - 1));
                return NGX_ERROR;
            }
        }

    next:

        continue;
    }

    return NGX_OK;
}

该函数主要是解析传入的参数并根据传入的参数解析。

ngx_signal_process信号发送函数

该函数主要是进行对worker进程的相关的信号的发送,其处理逻辑与kill的信号发送命令一样。

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];

    ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "signal process started");

    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;

    file.fd = ngx_open_file(file.name.data, NGX_FILE_RDONLY,
                            NGX_FILE_OPEN, NGX_FILE_DEFAULT_ACCESS);            // 打开文件

    if (file.fd == NGX_INVALID_FILE) {                                          // 检查是否是合法的pid
        ngx_log_error(NGX_LOG_ERR, cycle->log, ngx_errno,
                      ngx_open_file_n " \"%s\" failed", file.name.data);
        return 1;
    }

    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);
    }

    if (n == NGX_ERROR) {
        return 1;
    }

    while (n-- && (buf[n] == CR || buf[n] == LF)) { /* void */ }

    pid = ngx_atoi(buf, ++n);                                                   // 获取pid号

    if (pid == (ngx_pid_t) NGX_ERROR) {                                         // 检查是否出错
        ngx_log_error(NGX_LOG_ERR, cycle->log, 0,
                      "invalid PID number \"%*s\" in \"%s\"",
                      n, buf, file.name.data);
        return 1;
    }

    return ngx_os_signal_process(cycle, sig, pid);                              // 调用信号处理函数

}




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) {                  // 调用kill 给Pid发送相关信号
                return 0;
            }

            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
                          "kill(%P, %d) failed", pid, sig->signo);
        }
    }

    return 1;
}

主要工作就是根据传入的参数,给进程发送信号。

ngx_single_process_cycle单个进程启动

ngx_single_process_cycle该函数就是单个进程启动,

void
ngx_single_process_cycle(ngx_cycle_t *cycle)
{
    ngx_uint_t  i;

    if (ngx_set_environment(cycle, NULL) == NULL) {                 // 设置环境变量
        /* fatal */
        exit(2);                                                    // 如果失败则退出
    }

    for (i = 0; cycle->modules[i]; i++) {                           // 初始化给个module
        if (cycle->modules[i]->init_process) {
            if (cycle->modules[i]->init_process(cycle) == NGX_ERROR) {   // 调用各个module的init_process方法
                /* fatal */
                exit(2);
            }
        }
    }

    for ( ;; ) {
        ngx_log_debug0(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "worker cycle");

        ngx_process_events_and_timers(cycle);                       // 添加时间循环与事件处理 待后文分析

        if (ngx_terminate || ngx_quit) {                            // 如果退出

            for (i = 0; cycle->modules[i]; i++) {
                if (cycle->modules[i]->exit_process) {
                    cycle->modules[i]->exit_process(cycle);         // 调用各个module的退出
                }
            }

            ngx_master_process_exit(cycle);                         // 主进程退出
        }

        if (ngx_reconfigure) {                                      // 重新配置参数
            ngx_reconfigure = 0;
            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;
        }

        if (ngx_reopen) {                                           // 重新打开日志文件
            ngx_reopen = 0;
            ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "reopening logs");
            ngx_reopen_files(cycle, (ngx_uid_t) -1);
        }
    }
}

主要工作就是完成,注册事件的添加与定时器的相关操作,启动有关定时器和event的事件注册调度机制,待后文分析。至此一个基本的nginx就启动了,基本的启动流程如上所述。

总结

本文主要就是大致了解了一下nginx的启动的过程,其中启动过程中的相关配套的初始化过程,参数的解析,相关信号的注册,启动运行的模式等过程,后续会继续深入分析nginx的相关机制与运行的原理,鉴于本人才疏学浅,如有疏漏请批评指正。

猜你喜欢

转载自blog.csdn.net/qq_33339479/article/details/89154354