nginx代码分析--启动流程

 nginx代码分析--启动流程

一、  nginx的编译安装

1.     configure时干的事

根据configure后面跟的参数,解析auto文件夹下的文件生成Makefile文件。

2.     编译时干的事

ngx_modules.c文件是在编译过程中生成的,里面定义了ngx_modules[]模块数组,包含了哪些模块。

(安装及配置可以参考)http://www.cnblogs.com/geekma/archive/2012/07/28/2612951.html

二、  nginx启动流程

1.    模块相关

ngx_conf_s::module_type有四种类型:

NGX_CORE_MODULE,

NGX_EVENT_MODULE,

NGX_HTTP_MODULE,

NGX_MAIL_MODULE。

ngx_conf_s::cmd_type有十一种类型:

NGX_MAIN_CONF,

NGX_EVENT_CONF,

NGX_HTTP_MAIN_CONF,

NGX_HTTP_SRV_CONF,

NGX_HTTP_LOC_CONF,

NGX_HTTP_LMT_CONF,

NGX_HTTP_SIF_CONF,

NGX_HTTP_LIF_CONF,

NGX_HTTP_UPS_CONF,

NGX_MAIL_MAIN_CONF,

NGX_MAIL_SRV_CONF。

2.    一些数据结构

在介绍启动流程之前,先对几个很重要的数据结构进行下介绍。

typedef struct {
     /*daemon:是否为守护进程,默认为on,调试时会用off*/
     ngx_flag_t               daemon;
     /*master:是否启动master进程,默认为on,调试时会用off*/
     ngx_flag_t               master;
     /*timer_resolution:调用gettimeofday的间隔,可以减少调用次数*/
     ngx_msec_t               timer_resolution;
     /*worker_processes:工作进程数目*/
     ngx_int_t                worker_processes;
     /*debug_points:在调试器内设置断点等*/
     ngx_int_t                debug_points;
     /*rlimit_nofile:该进程内能够打开的文件描述符最大值*/
     ngx_int_t                rlimit_nofile;
     /*rlimit_sigpending:调用进程中真实用户队列的信号数量限制*/
     ngx_int_t                rlimit_sigpending;
     /*rlimit_core:允许每个进程核心文件的最大值*/
     off_t                    rlimit_core;
     /*priority:工作进程的优先级*/
     int                      priority;
     /*cpu_affinity_n:cpu绑定的个数*/
     ngx_uint_t               cpu_affinity_n;
     /*cpu_affinity:绑定的cpu*/
     uint64_t                *cpu_affinity;
     /*username:用户名*/
     char                    *username;
     /*user:用户ID*/
     ngx_uid_t                user;
     /*group:用户组ID*/
     ngx_gid_t                group;
     /*working_directory:工作的目录*/
     ngx_str_t                working_directory;
     /*lock_file:锁文件路径名*/
     ngx_str_t                lock_file;
     /*pid:进程号文件路径名*/
     ngx_str_t                pid;
     /*oldpid:老的进程号文件路径名*/
     ngx_str_t                oldpid;
     /*env:运行上下文*/
     ngx_array_t              env;
     /*environment:环境变量*/
     char                   **environment;

#if (NGX_THREADS)
     ngx_int_t                worker_threads;
     size_t                   thread_stack_size;
#endif
} ngx_core_conf_t;

一个实际进程打印出的ngx_cofe_conf_t结构体对象:

 

图:ngx_core_conf_t的实际值

struct ngx_cycle_s {
     /*conf_ctx: 配置上下文数组*/
    void                  ****conf_ctx;
     /*pool:资源池*/
    ngx_pool_t               *pool;
    /*log:日志相关的结构,包含了level,文件,handler等*/
    ngx_log_t                *log;
    ngx_log_t                 new_log;
    /*files:所有的连接*/
    ngx_connection_t        **files;
    /*free_connetions:空闲的连接*/
    ngx_connection_t         *free_connections;
    /*free_connection_n:空闲的连接数*/
    ngx_uint_t                free_connection_n;
    /*reusable_connections_queue:复用连接队列,keepalive*/
    ngx_queue_t               reusable_connections_queue;
    /*listening:监听数组*/
    ngx_array_t               listening;
    /*pathes:路径数组,与cache操作相关*/
    ngx_array_t               pathes;
    /*open_files:打开的文件链表*/
    ngx_list_t                open_files;
    /*shared_memory:共享内存*/
    ngx_list_t                shared_memory;
    /*connection_n:连接个数,对应配置文件中的worker_connections*/
    ngx_uint_t                connection_n;
    /*files_n:打开文件个数*/
    ngx_uint_t                files_n;
    /*connections:连接事件的双向链表*/
    ngx_connection_t         *connections;
    /*read_events:读事件双向链表*/
    ngx_event_t              *read_events;
    /*write_events:写事件双向链表*/
    ngx_event_t              *write_events;
    /*old_cycle:老的cycle变量*/
    ngx_cycle_t              *old_cycle;
    /*conf_file:配置文件*/
    ngx_str_t                 conf_file;
    /*conf_param:配置参数*/
    ngx_str_t                 conf_param;
    /*conf_prefix:配置前缀,配置文件路径*/
    ngx_str_t                 conf_prefix;
    /*prefix:前缀,默认为安装路径*/
    ngx_str_t                 prefix;
    /*lock_file:锁文件*/
    ngx_str_t                 lock_file;
    /*hostname:主机名*/
    ngx_str_t                 hostname;
};

 

图:ngx_cycle_s实际值

3.    进程启动

看任何C/C++代码,基本都从main函数入手,nginx的main函数在nginx.c文件中。下面,结合图3-1简单介绍下nginx的启动流程。

 

图3-1:启动流程图

main函数中,先初始化debug和error参数,然后才开始处理nginx启动时的参数选项(ngx_get_option),后面是其它各种初始化,其中有一段代码:

ngx_max_module = 0;
for (i = 0; ngx_modules[i]; i++) {
    ngx_modules[i]->index = ngx_max_module++;
}

这里面的ngx_modules数组在搜索代码时没有发现其相应的初始化部分,刚开始很奇怪,后来发现在编译完的代码中,有一个名为ngx_modules.c的文件,该文件中有这个数组的初始化操作,它是根据configure中的配置项来决定使用哪些模块的。这里,只是对调用的模块标示一下序号。

ngx_init_cycle函数是根据之前的ngx_cycle_t对象生成一个新的对象,这个结构体在整个进程中是一个很重要的结构。

在一系列初始化操作之后,会根据ngx_process的值来判断是单进程处理还是主进程处理(多进程)。

如果ngx_process == NGX_PROCESS_SINGLE,即单进程处理,则进入ngx_single_process_cycle函数。先初始化每个模块,然后进入一个无限循环for(;;),一方面可以对事件和计时器进行操作ngx_process_events_and_timers,同时也可以接收执行用户的操作命令ngx_terminate|ngx_quit|ngx_reconfigure|ngx_reopen。

如果ngx_process != NGX_PROCESS_SINGLE,即主进程处理,则进入ngx_master_process_cycle函数。同样也是先进行进程的初始化操作,然后调用ngx_start_worker_processes函数启动工作进程,调用ngx_start_cache_manager_processes函数启动cache管理进程,最后同样进入了无限循环for(;;),主进程的主要工作是接收用户命令并发送给各工作进程,同时也对各工作进程进行监视,在子进程(工作进程)退出时会发送SIGCHLD信号,致使ngx_reap为1,就会有如下处理:

if (ngx_reap) {
    ngx_reap = 0;
    ngx_log_debug0(NGX_LOG_DEBUG_EVENT, cycle->log, 0, "reap children");
    live = ngx_reap_children(cycle);
}

代码3-1:ngx_reap为1的处理

ngx_start_worker_processes工作进程的个数在conf文件中已经指定, for循环内ngx_spawn_process创建进程并执行ngx_worker_process_cycle函数,发现ngx_worker_process_cycle函数和ngx_single_process_cycle函数很相像,其实他们的工作原理基本是一样的,一方面处理事件和定时器ngx_process_events_and_timers,一方面接收执行用户命令,这个用户命令并非用户直接发送的,而是通过了master进程进行了中转。

启动流程到此为止,其它的事就交给了各进程的无限循环for(;;)来处理了。

posted @ 2012-12-30 15:30  Geek_Ma  阅读(1944)  评论(2编辑  收藏  举报