Nginx启动(ngx_init_cycle)

来源:互联网 发布:js单选框默认选中 编辑:程序博客网 时间:2024/06/05 19:15

1. ngx_cycle_t结构

 

该结构在./src/core/ngx_cycle.h文件中定义,如下。

[cpp] view plaincopy
  1. struct ngx_cycle_s {  
  2.     void                  ****conf_ctx;  //配置上下文数组(含所有模块)  
  3.     ngx_pool_t               *pool;      //内存池  
  4.   
  5.     ngx_log_t                *log;       //日志  
  6.     ngx_log_t                 new_log;  
  7.   
  8.     ngx_connection_t        **files;     //连接文件  
  9.     ngx_connection_t         *free_connections;  //空闲连接  
  10.     ngx_uint_t                free_connection_n; //空闲连接个数  
  11.   
  12.     ngx_queue_t               reusable_connections_queue;  //再利用连接队列  
  13.   
  14.     ngx_array_t               listening;     //监听数组  
  15.     ngx_array_t               pathes;        //路径数组  
  16.     ngx_list_t                open_files;    //打开文件链表  
  17.     ngx_list_t                shared_memory; //共享内存链表  
  18.   
  19.     ngx_uint_t                connection_n;  //连接个数  
  20.     ngx_uint_t                files_n;       //打开文件个数  
  21.   
  22.     ngx_connection_t         *connections;   //连接  
  23.     ngx_event_t              *read_events;   //读事件  
  24.     ngx_event_t              *write_events;  //写事件  
  25.   
  26.     ngx_cycle_t              *old_cycle;     //old cycle指针  
  27.   
  28.     ngx_str_t                 conf_file;     //配置文件  
  29.     ngx_str_t                 conf_param;    //配置参数  
  30.     ngx_str_t                 conf_prefix;   //配置前缀  
  31.     ngx_str_t                 prefix;        //前缀  
  32.     ngx_str_t                 lock_file;     //锁文件  
  33.     ngx_str_t                 hostname;      //主机名  
  34. };  

该结构体的大小是确定的,sizeof(ngx_cycle_t)=224

其中,

  • pathes数组元素结构为ngx_path_t
  • open_files链表元素结构为ngx_open_file_t
  • shared_memory链表元素结构为ngx_shm_zone_t
  • listening数组元素结构为ngx_listening_t,该数组用来存放监听套接字。

各种数据结构关系图如下。


2. ngx_init_cycle()分析

初始化过程如下。

  • 调用ngx_timezone_update()更新时区,调用ngx_time_update()更新时间
  • 创建大小为NGX_CYCLE_POOL_SIZE=16384B的内存池,并从中分配ngx_cycle_t结构
  • 简单初始化,如记录pool指针、log指针
  • 初始化配置前缀、前缀、配置文件、配置参数等字符串
  • 初始化pathes数组
  • 初始化open_files链表
  • 初始化shared_memory链表
  • 初始化listening数组
  • 初始化resuable_connections_queue队列
  • poolconf_ctx分配空间
  • 初始化hostname字符串
  • 调用core模块的create_conf()
  • 配置文件解析
  • 调用core模块的init_conf()
  • 遍历open_files链表中的每一个文件并打开
  • 创建共享内存并初始化(新旧shared_memory链表的比较,相同的共享内存保留,旧的不同的共享内存被释放,新的被创建)
  • (尝试5)遍历listening数组并打开所有侦听sockets(socket()->setsockopt()->bind()->listen())
  • 提交新的cycle配置,并调用所有模块的init_module(实际上只有ngx_event_core_module模块定义了该callback,即只有ngx_event_module_init()被调用)
  • 关闭或删除残留在old_cycle中的资源
    • 释放多余的共享内存
    • 关闭多余的侦听sockets
    • 关闭多余的打开文件 
3.          部分代码分析
ngx_timezone_update();
/* force localtime update with a new timezone */
tp = ngx_timeofday();
tp->sec = 0;
ngx_time_update();
这几个函数都是来自于Nginx的时间管理,对时区和时间进行一次更新操作。一个高性能服务器需要合理的调用gettimeofday(),减少gettimeofday()的调用次数有利于提高服务器的性能

/*创建一个大小为NGX_CYCLE_POOL_SIZE的内存池*/
pool = ngx_create_pool(NGX_CYCLE_POOL_SIZE, log);
if (pool == NULL) {
return NULL;
}
pool->log = log;
/*在内存池上分配一个ngx_cycle_t对象*/
cycle = ngx_pcalloc(pool, sizeof(ngx_cycle_t));
if (cycle == NULL) {
ngx_destroy_pool(pool);
return NULL;
}

它首先创建了一个内存池,然后在这个内存池上为cycle变量分配了一块存储空间。后续的所有初始化工作都为了填写这个cycle变量的各个成员字段。Nginx的内存池实现是相当的简单,但绝对不简陋;在web server的应用场景中是非常的有效。内存池的实现在src/core/ngx_palloc.h和src/core/ngx_palloc.c中。

/*初始化一个链表*/
if (ngx_list_init(&cycle->open_files, pool, n,
sizeof(ngx_open_file_t)) != NGX_OK)
{
ngx_destroy_pool(pool);
return NULL;
}
if (ngx_list_init(&cycle->shared_memory, pool, n,
sizeof(ngx_shm_zone_t)) != NGX_OK)
{
ngx_destroy_pool(pool);
return NULL;
}
Nginx使用了链表来维护需要打开的文件以及共享内存,也就是每个打开的文件都会放到cycle中的open_files链表中,每个共享内存段都会放到shared_memory链表中。

 /*初始化listening数组*/
cycle->listening.elts = ngx_pcalloc(pool, n * sizeof(ngx_listening_t));
if (cycle->listening.elts == NULL) {
ngx_destroy_pool(pool);
return NULL;
}
cycle->listening.nelts = 0;
cycle->listening.size = sizeof(ngx_listening_t);
cycle->listening.nalloc = n;
    cycle->listening.pool = pool;

为该数组分配起n个存储ngx_listening_t元素的单元。这个数组将用于存储监听套接字
 
 cycle->conf_ctx = ngx_pcalloc(pool, ngx_max_module * sizeof(void *));/*ngx_max_module为模块总数*/
if (cycle->conf_ctx == NULL) {
ngx_destroy_pool(pool);
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->create_conf) {
rv = module->create_conf(cycle);
if (rv == NULL) {
ngx_destroy_pool(pool);
return NULL;
}
cycle->conf_ctx[ngx_modules[i]->index] = rv;
}
}
首先将conf_ctx初始化了一段内存空间(可以看成一个普通的数组),这段空间能够存储ngx_max_module个void*指针;在启动初初始化过程(一)中,我们得知ngx_max_modules统计了模块总数,因此可以看出cycle中的conf_ctx将存储每个module的某些信息。接下来的for循环验证了我们的猜想。在for循环中,调用NGX_CORE_MODULE类型模块的ceate_conf回调,创建相应的配置结构存储空间,然后将这个配置结构存储空间的地址保存到conf_ctx数组的对应单元处。寻找正确的对应单元就是通过每个模块的index字段。通过对NGX_CORE_MODULE类型模块的处理,我们暂且猜测conf_ctx数组就是用来存储每个模块的配置结构;利用这个数组,我们能够获取每个模块相应的配置数据

NGX_CORE_MODULE类型的模块有:ngx_core_module、ngx_errlog_module、ngx_events_module和ngx_http_module等。

ngx_core_module定义在src/core/nginx.c文件中,create_conf钩子对应的函数为ngx_core_module_create_conf(),此函数就是创建ngx_core_conf_t配置结构。

ngx_errlog_module定义在src/core/ngx_log.c中,create_conf钩子没有对应的回调函数,为NULL。

ngx_events_module定义在src/event/ngx_event.c中,create_conf钩子没有对应的回调函数,为NULL。

ngx_http_module定义在src/http/ngx_http.c中,create_conf钩子没有对应的回调函数,为NULL。

由此可以看出,此处的循环执行create_conf回调函数,其实就只调用了ngx_core_module_create_conf()。

——————————————————————————————————ngx_core_conf_t————————————————————————————————————————————

Core模块的配置结构

如前文所述,core模块的callback有两个create_conf()init_conf(),从名字就可以看出,一个创建配置结构,另一个初始化该配置结构。core模块的配置结构如下,存放配置文件中的核心指令,如deamonmaster等。

./src/core/ngx_cycle.h

[cpp] view plaincopy
  1. typedef struct {  
  2.      ngx_flag_t               daemon;  
  3.      ngx_flag_t               master;  
  4.   
  5.      ngx_msec_t               timer_resolution;  
  6.   
  7.      ngx_int_t                worker_processes;  
  8.      ngx_int_t                debug_points;  
  9.   
  10.      ngx_int_t                rlimit_nofile;  
  11.      ngx_int_t                rlimit_sigpending;  
  12.      off_t                    rlimit_core;  
  13.   
  14.      int                      priority;  
  15.   
  16.      ngx_uint_t               cpu_affinity_n;  
  17.      u_long                  *cpu_affinity;  
  18.   
  19.      char                    *username;            /* 用户名 */  
  20.      ngx_uid_t                user;                /* user ID */  
  21.      ngx_gid_t                group;               /* group ID*/  
  22.   
  23.      ngx_str_t                working_directory;   /*  */  
  24.      ngx_str_t                lock_file;           /* 用户名 */  
  25.   
  26.      ngx_str_t                pid;  
  27.      ngx_str_t                oldpid;              /* 以'.oldbin'结尾 */  
  28.   
  29.      ngx_array_t              env;  
  30.      char                   **environment;  
  31.   
  32. #if (NGX_THREADS)  
  33.      ngx_int_t                worker_threads;  
  34.      size_t                   thread_stack_size;  
  35. #endif  
  36.   
  37. } ngx_core_conf_t;  

2.create_conf分析

static void *
ngx_core_module_create_conf(ngx_cycle_t *cycle)
{
    ngx_core_conf_t  *ccf;

    ccf = ngx_pcalloc(cycle->pool, sizeof(ngx_core_conf_t));
    if (ccf == NULL) {
        return NULL;
    }
    ccf->daemon = NGX_CONF_UNSET;
    ccf->master = NGX_CONF_UNSET;
    ccf->timer_resolution = NGX_CONF_UNSET_MSEC;

    ccf->worker_processes = NGX_CONF_UNSET;
    ccf->debug_points = NGX_CONF_UNSET;

    ccf->rlimit_nofile = NGX_CONF_UNSET;
    ccf->rlimit_core = NGX_CONF_UNSET;
    ccf->rlimit_sigpending = NGX_CONF_UNSET;

    ccf->user = (ngx_uid_t) NGX_CONF_UNSET_UINT;
    ccf->group = (ngx_gid_t) NGX_CONF_UNSET_UINT;

——————————————————————————————————结束—————————————————————————————————————————————————

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;
}
这里将完成对配置文件的解析工作,解析配置文件的时候,将会完成每个指令的set回调,这个set回调函数一般都是用于将配置数据填写到配置结构中。 解析配置文件常用的手法之一就是利用状态机,但此处Nginx却没有明确的使用状态机来完成配置文件的解析工作。后面具体看看Nginx解析配置文件的流程。

 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;
}
}
}

前面完成了NGX_CORE_MODULE类型模块的配置结构创建工作,这里调用所有NGX_CORE_MODULE类型模块的init_conf回调函数,完成配置结构的初始化工作。同ceate_conf,其实此处也只是调用了ngx_core_module的ngx_core_module_init_conf()回调函数。对ceate_conf创建的配置结构进行初始化。其他几个NGX_CORE_MODULE没有init_conf回调函数。

———————————————————————————————init_conf———————————————————————————————————————

3.init_conf
static char *
ngx_core_module_init_conf(ngx_cycle_t *cycle, void *conf)
{
    ngx_core_conf_t  *ccf = conf;

    //初始化daemon、master等
    ngx_conf_init_value(ccf->daemon, 1);
    ngx_conf_init_value(ccf->master, 1);
    ngx_conf_init_msec_value(ccf->timer_resolution, 0);

    ngx_conf_init_value(ccf->worker_processes, 1);
    ngx_conf_init_value(ccf->debug_points, 0);

 
————————————————————————————————结束———————————————————————————————————————————————————
 
/* open the new files */
part = &cycle->open_files.part;
file = part->elts;
/*此处for循环是在遍历链表open_files*/
for (i = 0; /* void */ ; i++) {
if (i >= part->nelts) {
if (part->next == NULL) {
break;
}
part = part->next;
file = part->elts;
i = 0;
}
if (file[i].name.len == 0) {
continue;
}
/*根据链表中存储的文件名来打开对应的文件*/
file[i].fd = ngx_open_file(file[i].name.data,
NGX_FILE_APPEND,
NGX_FILE_CREATE_OR_OPEN,
NGX_FILE_DEFAULT_ACCESS);
 
if (file[i].fd == NGX_INVALID_FILE) {
goto failed;
}
#if !(NGX_WIN32)
if (fcntl(file[i].fd, F_SETFD, FD_CLOEXEC) == -1) {
goto failed;
}
#endif
}

 遍历open_files链表,打开所有文件(调用open)。起初,我们看到了对open_files链表的创建、初始化,却没有看到写需要打开的文件名数据到链表中;其实,填写文件名数据就是在解析配置文件的时候完成的。除此之外,很多很多的数据初始化都是在解析配置文件的时候完成。执行了打开文件操作后,open_files链表就不光保存了文件名了,还保存了文件描述符等信息,足够以后读写文件之用了。
/* create shared memory */
part = &cycle->shared_memory.part;
shm_zone = part->elts;
/*同上面的open_files,此处遍历链表shared_memory,初始化各个共享内存段*/
for (i = 0; /* void */ ; i++) {
if (i >= part->nelts) {
if (part->next == NULL) {
break;
}
part = part->next;
shm_zone = part->elts;
i = 0;
}
if (shm_zone[i].shm.size == 0) {
goto failed;
}
if (shm_zone[i].init == NULL) {
/* unused shared zone */
continue;
}
shm_zone[i].shm.log = cycle->log;
opart = &old_cycle->shared_memory.part;
oshm_zone = opart->elts;
for (n = 0; /* void */ ; n++) {
if (n >= opart->nelts) {
if (opart->next == NULL) {
break;
}
opart = opart->next;
oshm_zone = opart->elts;
n = 0;
}
if (shm_zone[i].shm.name.len != oshm_zone[n].shm.name.len) {
continue;
}
if (ngx_strncmp(shm_zone[i].shm.name.data,
oshm_zone[n].shm.name.data,
shm_zone[i].shm.name.len)
!= 0)
{
continue;
}
if (shm_zone[i].shm.size == oshm_zone[n].shm.size) {
shm_zone[i].shm.addr = oshm_zone[n].shm.addr;
if (shm_zone[i].init(&shm_zone[i], oshm_zone[n].data)
!= NGX_OK)
{
goto failed;
}
goto shm_zone_found;
}
ngx_shm_free(&oshm_zone[n].shm);
break;
}
/*下面三步就完成共享内存的创建和初始化*/
if (ngx_shm_alloc(&shm_zone[i].shm) != NGX_OK) {
goto failed;
}
if (ngx_init_zone_pool(cycle, &shm_zone[i]) != NGX_OK) {
goto failed;
}
if (shm_zone[i].init(&shm_zone[i], NULL) != NGX_OK) {
goto failed;
}
shm_zone_found:
continue;
}

这部分复杂的代码就是为完成所有共享内存的创建及初始化,后面再专门分析Nginx对共享内存的管理及使用。

if (ngx_open_listening_sockets(cycle) != NGX_OK) {
goto failed;
}
if (!ngx_test_config) {
ngx_configure_listening_sockets(cycle);
}

遍历listening数组,打开所有的监听套接口(依次进行socket、bind、listen),同时设置一些socket选项及文件描述符属性,如非阻塞等

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);
}
}
}

执行所有模块的init_module操作,看名字为对模块进行初始化。 浏览源码,发现包括几个NGX_CORE_MODULE类型的模块在内的绝大多数模块都没有这个init回调函数。究竟哪些模块才使用这个回调接口呢?动用搜索功能,终于找到了一个模块使用了这个回调接口,它就是ngx_event_core_module。



 
 
 
0 0
原创粉丝点击