http://blog.csdn.net/chosen0ne/article/details/7728294
1. 重要的数据结构
1. ngx_module_t
nginx中所有模块的类型都是ngx_module_t类型的,定义了模块的一些属性。nginx是完全模块化的,所有的组件都是模块,从而实现了nginx的高度松耦合。同时,我们在进行nginx模块开发时,也离不开这个数据结构。
- struct ngx_module_s {
- /**
- * 在具体类型模块(http、event等)的全局配置结构数组的下标。以http module模块为例,
- * nginx把所有的http module的config信息存放在ngx_http_conf_ctx_t类型的变量中,
- * 这个变量只有3个属性,分别是所有http module的main、srv、loc的config信息的数组。
- * 如果该模块是http module,则ctx_index是该模块的config信息(main、srv、loc)
- * 在ngx_http_conf_ctx_t中的下标。
- */
- ngx_uint_t ctx_index;
- /**
- * nginx把所有模块(ngx_module_t)存放到ngx_modules数组中,这个数组在nginx源码路
- * 径的objs/ngx_modules.c中,是在运行configure脚本后生成的。index属性就是该模块
- * 在ngx_modules数组中的下标。同时nginx把所有的core module的配置结构存放到ngx_cycle的
- * conf_ctx数组中,index也是该模块的配置结构在ngx_cycle->conf_ctx数组中的下标。
- */
- ngx_uint_t index;
- ……
- /**
- * 模块的上下文属性,同一类型的模块的属性是相同的,比如core module的ctx是ngx_core_module_t类型。
- * 而http module的ctx是ngx_http_moduel_t类型,event module的ctx是ngx_event_module_t类型等等。
- * 相应类型的模块由分开处理的,比如所有的http module由ngx_http_module解析处理,而所有的event module
- * 由ngx_events_module解析处理。
- */
- void *ctx;
- /**
- * 该模块支持的指令的数组,最后以一个空指令结尾。ngx_commond_t的分析见下文。
- */
- ngx_command_t *commands;
- /**
- * 模块的类型,nginx所有的模块类型:
- * NGX_CORE_MODULE
- * NGX_CONF_MODULE
- * NGX_HTTP_MODULE
- * NGX_EVENT_MODULE
- * NGX_MAIL_MODULE
- * 这些不同的类型也指定了不同的ctx。
- */
- ngx_uint_t type;
- /* 接下来都是一些回调函数,在nginx初始化过程的特定时间点调用 */
- ngx_int_t (*init_master)(ngx_log_t *log);
- /* 初始化完所有模块后调用,在ngx_int_cycle函数(ngx_cycle.c)中 */
- ngx_int_t (*init_module)(ngx_cycle_t *cycle);
- /* 初始化完worker进程后调用,在ngx_worker_process_init函数(ngx_process_cycle.c)中 */
- ngx_int_t (*init_process)(ngx_cycle_t *cycle);
- ngx_int_t (*init_thread)(ngx_cycle_t *cycle);
- void (*exit_thread)(ngx_cycle_t *cycle);
- void (*exit_process)(ngx_cycle_t *cycle);
- void (*exit_master)(ngx_cycle_t *cycle);
- ……
- };
下面的表格是type与ctx的对应关系:
模块类型(type) | 上下文属性类型(ctx) |
NGX_CORE_MODULE | ngx_core_module_t(ngx_conf_file.h) |
NGX_CONF_MODULE | NULL |
NGX_HTTP_MODULE | ngx_http_module_t(http/ngx_http_config.h) |
NGX_EVENT_MODULE | ngx_event_module_t(event/ngx_event.h) |
NGX_MAIL_MODULE | ngx_mail_module_t(mail/ngx_mail.h) |
2. ngx_commond_t
ngx_commond_t描述的是模块的配置指令,也就是出现在配置文件的指令。nginx模块支持多个配置指令,所以是以ngx_commond_t数组形式存储的。这个结构在配置文件解析和模块的配置结构信息初始化时会用到。
- struct ngx_command_s {
- /**
- * 指令名,与配置文件中一致
- */
- ngx_str_t name;
- /**
- * 指令的类型,以及参数的个数。这个属性有两个作用:
- * 1. 实现只解析某个类型的指令,比如当前这个指令是event module类型的,而正在解析的是
- * http module,所以会跳过所有不是http module类型的指令。
- * 2. 实现指令参数个数的校验。
- */
- ngx_uint_t type;
- /*
- * 回调函数,在解析配置文件时,遇到这个指令时调用。
- * cf: 包括配置参数信息cf->args(ngx_array_t类型),以及指令对应的模块上下文cf->ctx
- * 在解析不同模块的指令时,这个上下文信息不同。比如在解析core module时,cf->ctx
- * 是ngx_cycle->conf_ctx也就是所有core module的配置结构数组,而在解析http module
- * 时cf->ctx是ngx_http_conf_ctx_t类型的,其中包含所有http module的main、srv、loc
- * 的配置结构数组。
- * cmd: 指令对应的ngx_command_t结构。
- * conf:指令对应的模块的配置信息。
- */
- char *(*set)(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
- /**
- * 对http module有效,http module的配置结构信息(main、srv、loc)都存放在ngx_http_conf_ctx_t
- * 中对应的数组,conf属性指示这个指令的配置结构是main、srv还是loc。
- */
- ngx_uint_t conf;
- /**
- * 指令对应属性在模块配置结构中的偏移量。
- */
- ngx_uint_t offset;
- /**
- * 一般是函数指针,在set回调函数中调用。
- */
- void *post;
- };
3. ngx_cycle_t
ngx_cycle_t是nginx中最重要的数据结构,包含了全局的配置信息、所有监听的套接字、连接池、读写事件等。ngx_cycle_t相当于nginx的一个生命周期,从nginx启动后直到向nginx发送stop或者reload信号。nginx中有一个全局变量ngx_cycle指向当前的cycle。
- struct ngx_cycle_s {
- /**
- * 所有core module的配置结构信息的数组,大小是ngx_max_module。配置结构信息由
- * ngx_core_module_t的create_config回调函数生成,并有init_config回调函数初始化。
- * module的index属性指示了该模块的配置结构信息在conf_ctx数组中的下标。
- */
- void ****conf_ctx;
- /**
- * 随ngx_cycle_t同生命周期的内存池,是生命周期最长的,除非nginx关闭或重启。内存块大小
- * 由NGX_CYCLE_POOL_SIZE定义,是16KB。
- */
- 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_listening_t,ngx_create_listening函数会向这个数组
- * 添加新的监听句柄。
- */
- ngx_array_t listening;
- ngx_array_t pathes; // 路径数组,ngx_path_t
- /**
- * 所有打开的文件描述符的链表,ngx_open_file_t类型。ngx_conf_file.c中的
- * ngx_conf_open_file(ngx_cycyle_t *cycle, ngx_str_t *name)
- * 可以返回名字为name的打开的文件描述符,如果对应的描述符不存在,则打开并存入open_files中。
- * 比如:logs/error.log的文件描述符就在其中。
- */
- ngx_list_t open_files;
- /**
- * 共享内存的链表,ngx_shm_zone_t类型。nginx会把需要进程共享的数据放在共享内存中。
- * 比如,accept锁就放在这里。worker进程只有获取这个锁之后,才能accept到新的连接。
- * 防止惊群。
- */
- ngx_list_t shared_memory;
- ngx_uint_t connection_n; // 链接个数
- ngx_uint_t files_n; // 打开文件个数
- /**
- * 连接池,nginx把连接池组织成综合数组和链表特性的一种数据结构,后文会详细介绍。
- */
- 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; // 由命令行-g提供配置参数
- ngx_str_t conf_prefix; // 配置前缀
- ngx_str_t prefix; // nginx所在路径
- ngx_str_t lock_file;
- ngx_str_t hostname; // 主机名
- };
2. nginx启动过程
nginx启动显然是由main函数驱动的,在core/nginx.c文件中,整个启动过程主要是初始化ngx_cycle_t类型的全局变量ngx_cycle。下面分析按照nginx的主要执行流程讲述的,首先从main函数开始。
(1) main函数
- if (ngx_strerror_init() != NGX_OK) {
- return 1;
- }
- if (ngx_get_options(argc, argv) != NGX_OK) {
- return 1;
- }
- ngx_time_init();
- (NGX_PCRE)
- ngx_regex_init();
- if
- ngx_pid = ngx_getpid();
- // 初始化log
- log = ngx_log_init(ngx_prefix);
- if (log == NULL) {
- return 1;
- }
- /*
- * init_cycle->log is required for signal handlers and
- * ngx_process_options()
- */
- ngx_memzero(&init_cycle, sizeof(ngx_cycle_t));
- init_cycle.log = log;
- ngx_cycle = &init_cycle;
- // cycle的内存池以1024大小的内存块
- init_cycle.pool = ngx_create_pool(1024, log);
- 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) {
- return 1;
- }
- if (ngx_os_init(log) != NGX_OK) {
- return 1;
- }
- if (ngx_crc32_table_init() != NGX_OK) {
- return 1;
- }
- if (ngx_add_inherited_sockets(&init_cycle) != NGX_OK) {
- return 1;
- }
- ngx_max_module = 0;
- for (i = 0; ngx_modules[i]; i++) {
- ngx_modules[i]->index = ngx_max_module++;
- }
- cycle = ngx_init_cycle(&init_cycle);
(2) ngx_init_cycle函数
在ngx_cycle_t结构中包含各种ngx_list_t、ngx_array_t等类型的属性,ngx_init_cycle的前面200多行代码都是对这些数据结构进行初始化。主要有
1. 更新时区和时间。
2. 创建内存池。
3. 根据old_cycle初始化cycle中的conf_file、conf_prefix、prefix和conf_param。
4. 初始化pathes(ngx_array_t)。
5. 初始化openfiles(ngx_list_t)。
6. 初始化shared_memory(ngx_list_t)。
7. 初始化listening(ngx_array_t)。
8. 初始化conf_ctx(void ****)数组,大小是ngx_max_module,用于存储所有core module的配置结构信息。
9. 调用系统调用gethostname获取主机名,初始化hostname。
介绍完ngx_init_cycle前面的大部分初始化工作后,下面看一下比较重要的初始化步骤。
- for (i = 0; ngx_modules[i]; i++) {
- if (ngx_modules[i]->type != NGX_CORE_MODULE) {
- continue;
- }
- module = ngx_modules[i]->ctx;
- if (module->create_conf) {
- rv = module->create_conf(cycle);
- if (rv == NULL) {
- ngx_destroy_pool(pool);
- return NULL;
- }
- cycle->conf_ctx[ngx_modules[i]->index] = rv;
- }
- }
ngx_core_module(core/nginx.c)
ngx_http_module(http/ngx_http.c)
ngx_events_module(event/ngx_event.c)
ngx_errlog_module(core/ngx_log.c)
ngx_mail_module(mail/ngx_mail.c)
ngx_openssl_module(event/ngx_event_openssl.c)
ngx_google_perftools_module(misc/ngx_google_perftools_module.c)
只有ngx_core_module和ngx_google_perftools_module两个模块有定义create_conf,而ngx_google_perftools_module仅用于性能测试,所以真正使用时只有ngx_core_module有create_conf回调函数。这个会调用函数会创建ngx_core_conf_t结构,用于存储整个配置文件main scope范围内的信息,比如worker_processes,worker_cpu_affinity等。
- ngx_memzero(&conf, sizeof(ngx_conf_t));
- /* STUB: init array ? */
- conf.args = ngx_array_create(pool, 10, sizeof(ngx_str_t)); // args是指令后带的参数的数组
- if (conf.args == NULL) {
- ngx_destroy_pool(pool);
- return NULL;
- }
- conf.temp_pool = ngx_create_pool(NGX_CYCLE_POOL_SIZE, log);
- if (conf.temp_pool == NULL) {
- ngx_destroy_pool(pool);
- return NULL;
- }
- /* 解析core module对应的指令,此时conf的上下文是cycle->conf_ctx,就是所有core module的config信息 */
- conf.ctx = cycle->conf_ctx;
- conf.cycle = cycle;
- conf.pool = pool;
- conf.log = log;
- conf.module_type = NGX_CORE_MODULE; // conf.module_type指示将要解析这个类型模块的指令。
- conf.cmd_type = NGX_MAIN_CONF; // conf.cmd_type指示将要解析的指令的类型。
- if (ngx_conf_param(&conf) != NGX_CONF_OK) {
- environ = senv;
- ngx_destroy_cycle_pools(&conf);
- return NULL;
- }
- if (ngx_conf_parse(&conf, &cycle->conf_file) != NGX_CONF_OK) {
- environ = senv;
- ngx_destroy_cycle_pools(&conf);
- return NULL;
- }
- for (i = 0; ngx_modules[i]; i++) {
- if (ngx_modules[i]->type != NGX_CORE_MODULE) {
- continue;
- }
- module = ngx_modules[i]->ctx;
- if (module->init_conf) {
- if (module->init_conf(cycle, cycle->conf_ctx[ngx_modules[i]->index])
- == NGX_CONF_ERROR)
- {
- environ = senv;
- ngx_destroy_cycle_pools(&conf);
- return NULL;
- }
- }
- }
- if (ngx_test_config) {
- if (ngx_create_pidfile(&ccf->pid, log) != NGX_OK) {
- goto failed;
- }
- } else if (!ngx_is_init_cycle(old_cycle)) {
- old_ccf = (ngx_core_conf_t *) ngx_get_conf(old_cycle->conf_ctx,
- ngx_core_module);
- if (ccf->pid.len != old_ccf->pid.len
- || ngx_strcmp(ccf->pid.data, old_ccf->pid.data) != 0)
- {
- if (ngx_create_pidfile(&ccf->pid, log) != NGX_OK) {
- goto failed;
- }
- ngx_delete_pidfile(old_cycle);
- }
- }
- if (ngx_open_listening_sockets(cycle) != NGX_OK) {
- goto failed;
- }
- if (!ngx_test_config) {
- ngx_configure_listening_sockets(cycle);
- }
- for (i = 0; ngx_modules[i]; i++) {
- if (ngx_modules[i]->init_module) {
- if (ngx_modules[i]->init_module(cycle) != NGX_OK) {
- /* fatal */
- exit(1);
- }
- }
- }
(3) main函数
- 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_create_pidfile(&ccf->pid, cycle->log) != NGX_OK) {
- return 1;
- }
- if (ngx_process == NGX_PROCESS_SINGLE) {
- ngx_single_process_cycle(cycle);
- } else {
- ngx_master_process_cycle(cycle);
- }