Nginx源码分析:启动流程
发布日期:2021-07-25 13:04:55 浏览次数:14 分类:技术文章

本文共 13431 字,大约阅读时间需要 44 分钟。

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_cdeclmain(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_tngx_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_tngx_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_tngx_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该函数就是单个进程启动,

voidngx_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的相关机制与运行的原理,鉴于本人才疏学浅,如有疏漏请批评指正。

转载地址:https://blog.csdn.net/qq_33339479/article/details/89154354 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:Nginx源码分析:master/worker工作流程概述
下一篇:代码覆盖率原理分析:sys.settrace流程分析

发表评论

最新留言

不错!
[***.144.177.141]2024年02月29日 03时02分52秒

关于作者

    喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!

推荐文章

hdfs linux 目录是否存在,Linux中判断hdfs文件是否存在 2019-04-21
linux学习需要什么基础,学linux需要什么基础? 2019-04-21
linux vim编辑kconfig 无法wq,Linux-4.9.2内核在mini2440上的移植(三)——编译环境测试... 2019-04-21
高斯勒让德在c语言中的程序,c语言:用递归方法编写程序,求n阶勒让德多项式的值... 2019-04-21
c语言单片机电子时钟,新人求个51单片机的电子时钟汇编语言(C语言的还没学到)... 2019-04-21
c++语言文件流,C++文件流 2019-04-21
android 动态毛玻璃,Android毛玻璃背景效果简单实现代码 2019-04-21
android 按钮提示,的Android按钮工具提示 2019-04-21
iphone通讯录 android,3个方法,教你如何快速而又有效的将联系人从iPhone转移到安卓... 2019-04-21
android horizontalscrollview 滑动事件,ScrollView的滑动监听(以HorizontalScrollView为例) 2019-04-21
win7自定义html为桌面,Win7系统自定义桌面主题的方法 2019-04-21
单系统 台电x80pro_台电x80 pro (ID:E3E6)安装remix OS系统教程整理 2019-04-21
linux存储pdf伟岸_python的reportlab库介绍、制作pdf和作图 2019-04-21
安徽信息技术初中会考上机考试模拟_2020年中小学寒假、考试时间定下了! 2019-04-21
ubuntu 退出anaconda环境_从零开始深度学习第15讲:ubuntu16.04 下深度学习开发环境搭建与配置... 2019-04-21
稳定币usda是哪个发行的_武夷山币装帧款曝光,共4款设计,你喜欢哪款? 2019-04-21
可变车道怎么走不违章_走ETC竟比人工车道贵50%!交警:这3点不知道,吃亏的是自己... 2019-04-21
苹果笔记本的end键_笔记本用户的大烦恼:触控板,想好好用你不容易 2019-04-21
趣玩机器人什么时候成立的_【直播回顾】当我们谈机器人集成调试的时候在谈什么... 2019-04-21
中考大数据大连79_中考大数据 | 大连部分初中2019年中考指标生录取最低分及人数统计!... 2019-04-21