nginx代码分析--启动流程
一、 nginx的编译安装
1. configure时干的事
根据configure后面跟的参数,解析auto文件夹下的文件生成Makefile文件。
2. 编译时干的事
ngx_modules.c文件是在编译过程中生成的,里面定义了ngx_modules[]模块数组,包含了哪些模块。
(安装及配置可以参考)
二、 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(;;)来处理了。