且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

Nginx学习笔记(六) 源码分析&启动过程

更新时间:2022-09-19 07:55:09

主要介绍Nginx的启动过程,可以在/core/nginx.c中找到Nginx的主函数main(),那么就从这里开始分析Nginx的启动过程。

涉及到的基本函数

源码:

Nginx学习笔记(六) 源码分析&启动过程 View Code

  Nginx的启动包括了很多的初始化和处理函数。这些函数相对来说,有一部分非常复杂,暂且从简单开始,从整体上对Ngixnd的启动有一个了解,方便日后的分析与学习。

  主要函数:

Nginx学习笔记(六) 源码分析&启动过程
//完成socket的继承
static
ngx_int_t ngx_add_inherited_sockets(ngx_cycle_t *cycle);
//对参数选项进行处理
static ngx_int_t ngx_get_options(int argc, char *const *argv);
//初始化ngx_cycle内的部分内容
static ngx_int_t ngx_process_options(ngx_cycle_t *cycle);
//命令行参数保存到ngx_os_argv、ngx_argc以及ngx_argv全局的变量中
static ngx_int_t ngx_save_argv(ngx_cycle_t *cycle, int argc, char *const *argv);
//创建模块的配置信息
static void *ngx_core_module_create_conf(ngx_cycle_t *cycle);
//初始化配置信息
static char *ngx_core_module_init_conf(ngx_cycle_t *cycle, void *conf); static char *ngx_set_user(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); static char *ngx_set_env(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
//设置优先级
static char *ngx_set_priority(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
//设置CPU亲和性
static char *ngx_set_cpu_affinity(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
//设置worker进程
static char *ngx_set_worker_processes(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
Nginx学习笔记(六) 源码分析&启动过程

Nginx启动的主要流程

   下图为Nginx的启动时函数的调用过程,其中大部分都是为了Nginx启动的初始化部分。从错误处理、参数设置、时间设置等方面进行初始化,并注册了我们需要的模块,最后根据信号选择单任务模式还是master-worker模式。

  流程图:

Nginx学习笔记(六) 源码分析&启动过程

初始化

  主函数在开始对系统错误、参数、时间、系统变量、日志等进行了初始化。

Nginx学习笔记(六) 源码分析&启动过程
//初始化系统中错误编号对应的含义
ngx_strerror_init();
//对参数选项进行处理
ngx_get_options(argc, argv);
//时间初始化
ngx_time_init();
//重置pcre内存管理的接口
ngx_regex_init();
//日志初始化
ngx_log_init(ngx_prefix);
//创建内存池
ngx_create_pool(1024, log);
//保存变量
ngx_save_argv();
//初始化ngx_cycle的prefix, conf_prefix, conf_file, conf_param
ngx_process_options();
//初始化系统相关变量,如内存页面大小ngx_pagesize,ngx_cacheline_size,最大连接数ngx_max_sockets等
ngx_os_init();
//初始化CRC表(后续的CRC校验通过查表进行,效率高)
ngx_crc32_table_init();
Nginx学习笔记(六) 源码分析&启动过程

主要工作

  初始化完成后,需要先调用ngx_add_inherited_sockets函数继承socket,并储存在Listening数组中,在运行时候进行监听。之后就可以调用ngx_init_cycle来初始化ngx_cycle结构体,这个结构体用来存储所有的连接,具体如下:

Nginx学习笔记(六) 源码分析&启动过程
struct ngx_cycle_s {
    void                  ****conf_ctx;//配置上下文数组(含所有模块)
    ngx_pool_t               *pool;//内存池

    ngx_log_t                *log;//日志
    ngx_log_t                 new_log;

    ngx_connection_t        **files;//连接文件    
    ngx_connection_t         *free_connections;//空闲连接
    ngx_uint_t                free_connection_n;//空闲连接数

    ngx_queue_t               reusable_connections_queue;////再利用连接队列  

    ngx_array_t               listening;//监听数组
    ngx_array_t               paths;//路径数组
    ngx_list_t                open_files;//打开文件链表
    ngx_list_t                shared_memory;//共享内存链表

    ngx_uint_t                connection_n;//连接个数  
    ngx_uint_t                files_n;//打开文件个数 

    ngx_connection_t         *connections;
    ngx_event_t              *read_events;//读事件    
    ngx_event_t              *write_events;//写事件

    ngx_cycle_t              *old_cycle;

    ngx_str_t                 conf_file;//配置文件  
    ngx_str_t                 conf_param;//配置参数
    ngx_str_t                 conf_prefix;//配置前缀
    ngx_str_t                 prefix;//前缀
    ngx_str_t                 lock_file;//锁文件
    ngx_str_t                 hostname;
Nginx学习笔记(六) 源码分析&启动过程

  调用ngx_init_signals来注册信号。

//信号种类
#define
NGX_PROCESS_SINGLE 0 #define NGX_PROCESS_MASTER 1 #define NGX_PROCESS_SIGNALLER 2 #define NGX_PROCESS_WORKER 3 #define NGX_PROCESS_HELPER 4

  在进入处理之前,还要调用ngx_create_pidfile来记录进程id。最后,根据接收到的信号,来判断调用ngx_single_process_cycle还是ngx_master_process_cycle(master-worker模式)。

if (ngx_process == NGX_PROCESS_SINGLE) {
        ngx_single_process_cycle(cycle);
} else {
        ngx_master_process_cycle(cycle);
}

 

  其中,守护进程函数为ngx_daemon,位于src/os/unix/Ngx_daemon.c

Nginx学习笔记(六) 源码分析&启动过程
//daemon
ngx_int_t ngx_daemon(ngx_log_t
*log) { int fd; switch (fork()) { case -1: ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, "fork() failed"); return NGX_ERROR; case 0: breakdefault: exit(0); } ngx_pid = ngx_getpid();//取得进程识别码 if (setsid() == -1) { //子进程将重新获得一个新的会话(session)id ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, "setsid() failed"); return NGX_ERROR; } umask(0); fd = open("/dev/null", O_RDWR); if (fd == -1) { ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, "open(\"/dev/null\") failed"); return NGX_ERROR; } if (dup2(fd, STDIN_FILENO) == -1) { ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, "dup2(STDIN) failed"); return NGX_ERROR; } if (dup2(fd, STDOUT_FILENO) == -1) { ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, "dup2(STDOUT) failed"); return NGX_ERROR; } #if 0 if (dup2(fd, STDERR_FILENO) == -1) { ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, "dup2(STDERR) failed"); return NGX_ERROR; } #endif if (fd > STDERR_FILENO) { if (close(fd) == -1) { ngx_log_error(NGX_LOG_EMERG, log, ngx_errno, "close() failed"); return NGX_ERROR; } } return NGX_OK; }
Nginx学习笔记(六) 源码分析&启动过程
本文转自cococo点点博客园博客,原文链接:http://www.cnblogs.com/coder2012/p/3166658.html,如需转载请自行联系原作者