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(;;)来处理了。