感觉很好的一片文章,转载了
一、 主流程
几乎所有的服务器程序的工作模式都是:
(1) 初始化一些参数;
(2) 开启监听socket;
(3) 在主线程的死循环(一般都是死循环)中对监听socket和业务socket进行IO状态监控(IO多路复用),并对IO进行处理;
Nginx的主要工作模式是多进程模式,那么它的工作方式也是类似的。只不过是它的死循环被分散到了各个工作进程中。
这里将从main函数切入,逐步对各个流程进行分析,由于nginx的功能比较强大,也就意味着实现比较复杂,这里将分模块对各个小逻辑的处理流程进行梳理。
mian函数入口的主流程调用关系如下图所示:
主流程的函数调用关系
各函数的调用关系说明:
1. Main函数在文件:src\core\nginx.c中;
2. 核心数据结构ngx_cycle_t(ngx_cycle_s)存储了Nginx的核心数据结构,包括内存池、日志等信息,nginx几乎所有的操作都围绕该数据结构进行;
3. 打开监听socket
函数调用关系
main=> ngx_init_cycle=>ngx_open_listening_sockets
4. 创建工作进程
主循环处理流程在函数:ngx_master_process_cycle或ngx_single_process_cycle中,其中的ngx_start_worker_processes函数主要创建并启动工作进程,在该函数中调用了ngx_spawn_process创建工作进程,工作进程所执行的代码为处理函数ngx_worker_process_cycle;
5. 工作进程函数:ngx_worker_process_cycle,在该函数中死循环调用:ngx_process_events_and_timers进行事件处理;
6. 在函数ngx_process_events_and_timers中,调用了宏ngx_process_events对事件进行处理,需要特别注意该宏,nginx通过它实现了对不同操作系统下各种IO复用方式进行统一处理,这一过程将会在下面详细描述。
二、 IO多路复用
Nginx支持epoll、select、kqueue等不同操作系统下的各种IO多路复用方式,本节将分析Nginx是如何实现在一套处理流程中同时兼容各种不同的IO多路复用方式?关于IO多路复用相关的描述可参考:http://blog.csdn.net/houjixin/article/details/27662489。
1. 以全局变量ngx_event_core_module为线索进行分析
在文件src\event\ngx_event.c中定义了全局变量ngx_event_core_module,类型为结构体ngx_module_t(即structngx_module_s),该全局变量包含了几乎nginx对所有事件操作的全部信息(包括所需的函数和全局变量),该全局变量在定义时被赋值了几个非常重要的全局变量和函数指针(见文件src\event\ngx_event.c):
ngx_module_t ngx_event_core_module = {
NGX_MODULE_V1,
&ngx_event_core_module_ctx, /* module context */
ngx_event_core_commands, /* module directives */
NGX_EVENT_MODULE, /* module type */
NULL, /* initmaster */
ngx_event_module_init, /* init module */
ngx_event_process_init, /* init process */
NULL, /* initthread */
NULL, /* exitthread */
NULL, /* exitprocess */
NULL, /* exitmaster */
NGX_MODULE_V1_PADDING
};
两个全局变量ngx_event_core_module_ctx和ngx_event_core_commands,两个函数指针ngx_event_module_init和ngx_event_process_init,如下图所示(下面的箭头表示包含关系):
这里,ngx_event_core_module所包含的全局变量ngx_event_core_commands(该变量类型为结构体structngx_command_s),它内部包含了一个函数指针ngx_event_use,如上图所示;
到现在为止,上文关注的全局变量ngx_event_core_module直接或间接包含的三个函数指针ngx_event_module_init、ngx_event_process_init和ngx_event_use都会对该全局变量进行操作。在nginx中只有6个函数操作了全局变量ngx_event_core_module:ngx_event_module_init、ngx_event_process_init、ngx_event_use、ngx_event_accept、ngx_poll_init_conf、ngx_select_init_conf,如下图所示:
ngx_event_core_module所包含的全局变量ngx_event_core_module_ctx的类型为结构体ngx_event_module_t,该结构体定义为:
typedef struct {
ngx_str_t *name;
void *(*create_conf)(ngx_cycle_t*cycle);
char *(*init_conf)(ngx_cycle_t*cycle, void *conf);
ngx_event_actions_t actions;
} ngx_event_module_t;
该结构体中的函数指针成员init_conf指向了初始化事件处理的操作函数,在全局变量ngx_event_core_module_ctx中该函数指针成员被赋值为函数:ngx_event_core_init_conf(见src\event\ngx_event.c)。nginx针对不同操作系统支持各种多路IO复用的方式,例如epoll、poll、kqueue、select,在该函数中,将根据OS的类型选择适用的事件及其处理函数,如果是epoll,则在编译时将会选择全局变量ngx_epoll_module,后文将以epoll为例描述如何以该全局变量为线索将epoll相关的操作串起来。如下图所示:
2. 全局变量ngx_event_actions
在前面的介绍中提到要重点关注宏ngx_process_events,在本节中将详细描述该宏如何适配不同的IO处理模型?不同OS所采用的IO多路复用技术的实现方式不同,并且每个IO处理模型都有自己独特的实现方式和接口,nginx采用如下方式来支持多种IO处理方式:
(1)首先将对各种不同的IO复用的处理方式进行抽象,每种处理方式都被抽象成一个函数指针,然后这些函数指针组成一个结构体,即:ngx_event_actions_t,结构体ngx_event_actions_t的定义为:
typedefstruct {
ngx_int_t (*add)(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags);
ngx_int_t (*del)(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags);
ngx_int_t (*enable)(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags);
ngx_int_t (*disable)(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags);
ngx_int_t (*add_conn)(ngx_connection_t *c);
ngx_int_t (*del_conn)(ngx_connection_t *c, ngx_uint_t flags);
ngx_int_t (*notify)(ngx_event_handler_pt handler);
ngx_int_t (*process_events)(ngx_cycle_t *cycle, ngx_msec_t timer,
ngx_uint_t flags);
ngx_int_t (*init)(ngx_cycle_t *cycle, ngx_msec_t timer);
void (*done)(ngx_cycle_t *cycle);
} ngx_event_actions_t;
(2)定义一个ngx_event_actions_t类型的全局变量ngx_event_actions用以保存实际的处理函数,例如:epoll、poll、kqueue等每种方式都会定义自己的处理函数;以epoll为例,在epoll的ngx_epoll_init函数中将所定义的epoll处理函数赋值给该全局变量ngx_event_actions,从而,我们前面重点关注的宏ngx_process_events也就被赋予了操作epoll的具体函数指针。
(3)在文件src\event\ngx_event.h中,又对ngx_event_actions中的各函数指针进行重新宏定义:
#definengx_process_events ngx_event_actions.process_events
#definengx_done_events ngx_event_actions.done
#definengx_add_event ngx_event_actions.add
#definengx_del_event ngx_event_actions.del
#definengx_add_conn ngx_event_actions.add_conn
#definengx_del_conn ngx_event_actions.del_conn
#definengx_notify ngx_event_actions.notify
而在nginx的代码中,都是使用这些宏来进行IO多路复用的操作,例如执行宏ngx_add_event,就是执行全局变量的add函数指针:ngx_event_actions.add,而全局变量ngx_event_actions又在初始化时被不同的IO多路复用方式赋值为各自的处理函数。我们前面重点关注的宏ngx_process_events就是在这里定义的。
3. 以epoll为例说明nginx如何进行网络事件处理?
Nginx支持多种网络处理方式,这里以常用的epoll为例说明其工作原理。根据前文所述,函数ngx_event_core_init_conf中调用了全局变量ngx_epoll_module,该结构体变量中将逐步引入其他epoll相关的变量或者函数,如下图所示:
在上图中,椭圆表示全局变量,方框表示函数或者函数指针;
(1)重点数据结构和全局变量
ngx_epoll_module变量的类型为结构体ngx_module_t,该结构体的定义为:
ngx_module_t ngx_epoll_module = {
NGX_MODULE_V1,
&ngx_epoll_module_ctx, /* module context */
ngx_epoll_commands, /* module directives */
NGX_EVENT_MODULE, /* module type */
NULL, /* init master*/
NULL, /* initmodule */
NULL, /* init process*/
NULL, /* init thread*/
NULL, /* exit thread*/
NULL, /* exit process */
NULL, /* exit master*/
NGX_MODULE_V1_PADDING
};
上述ngx_epoll_module结构体变量包含一个全局变量ngx_epoll_module_ctx,其类型为结构体ngx_event_module_t,该结构体定义为:
typedefstruct {
ngx_str_t *name;
void *(*create_conf)(ngx_cycle_t*cycle);
char *(*init_conf)(ngx_cycle_t*cycle, void *conf);
ngx_event_actions_t actions;
} ngx_event_module_t;
该结构体的ngx_event_actions_t类型的成员变量actions,保存了epoll对事件的所有相关处理函数,根据前文所述,这里保存的actions将被赋值给全局事件结构体变量ngx_event_actions。
在epoll的事件处理函数中上述结构体变量被进行了如下赋值(见文件src\event\modules\ngx_epoll_module.c):
ngx_event_module_t ngx_epoll_module_ctx = {
&epoll_name,
ngx_epoll_create_conf, /* create configuration */
ngx_epoll_init_conf, /* init configuration */
//下面用于设置epoll事件的所有处理函数到成员actions(类型为结构体ngx_event_actions_t)中
{
ngx_epoll_add_event, /* add an event */
ngx_epoll_del_event, /* delete an event */
ngx_epoll_add_event, /* enable an event */
ngx_epoll_del_event, /* disable an event */
ngx_epoll_add_connection, /* add an connection */
ngx_epoll_del_connection, /* delete an connection */
#if(NGX_HAVE_EVENTFD)
ngx_epoll_notify, /* trigger a notify */
#else
NULL, /* trigger a notify*/
#endif
ngx_epoll_process_events, /* process the events */
ngx_epoll_init, /* init the events */
ngx_epoll_done, /* done the events */
}
};
几乎所有的服务器程序的工作模式都是:
(1) 初始化一些参数;
(2) 开启监听socket;
(3) 在主线程的死循环(一般都是死循环)中对监听socket和业务socket进行IO状态监控(IO多路复用),并对IO进行处理;
Nginx的主要工作模式是多进程模式,那么它的工作方式也是类似的。只不过是它的死循环被分散到了各个工作进程中。
这里将从main函数切入,逐步对各个流程进行分析,由于nginx的功能比较强大,也就意味着实现比较复杂,这里将分模块对各个小逻辑的处理流程进行梳理。
mian函数入口的主流程调用关系如下图所示:
主流程的函数调用关系
各函数的调用关系说明:
1. Main函数在文件:src\core\nginx.c中;
2. 核心数据结构ngx_cycle_t(ngx_cycle_s)存储了Nginx的核心数据结构,包括内存池、日志等信息,nginx几乎所有的操作都围绕该数据结构进行;
3. 打开监听socket
函数调用关系
main=> ngx_init_cycle=>ngx_open_listening_sockets
4. 创建工作进程
主循环处理流程在函数:ngx_master_process_cycle或ngx_single_process_cycle中,其中的ngx_start_worker_processes函数主要创建并启动工作进程,在该函数中调用了ngx_spawn_process创建工作进程,工作进程所执行的代码为处理函数ngx_worker_process_cycle;
5. 工作进程函数:ngx_worker_process_cycle,在该函数中死循环调用:ngx_process_events_and_timers进行事件处理;
6. 在函数ngx_process_events_and_timers中,调用了宏ngx_process_events对事件进行处理,需要特别注意该宏,nginx通过它实现了对不同操作系统下各种IO复用方式进行统一处理,这一过程将会在下面详细描述。
二、 IO多路复用
Nginx支持epoll、select、kqueue等不同操作系统下的各种IO多路复用方式,本节将分析Nginx是如何实现在一套处理流程中同时兼容各种不同的IO多路复用方式?关于IO多路复用相关的描述可参考:http://blog.csdn.net/houjixin/article/details/27662489。
1. 以全局变量ngx_event_core_module为线索进行分析
在文件src\event\ngx_event.c中定义了全局变量ngx_event_core_module,类型为结构体ngx_module_t(即structngx_module_s),该全局变量包含了几乎nginx对所有事件操作的全部信息(包括所需的函数和全局变量),该全局变量在定义时被赋值了几个非常重要的全局变量和函数指针(见文件src\event\ngx_event.c):
ngx_module_t ngx_event_core_module = {
NGX_MODULE_V1,
&ngx_event_core_module_ctx, /* module context */
ngx_event_core_commands, /* module directives */
NGX_EVENT_MODULE, /* module type */
NULL, /* initmaster */
ngx_event_module_init, /* init module */
ngx_event_process_init, /* init process */
NULL, /* initthread */
NULL, /* exitthread */
NULL, /* exitprocess */
NULL, /* exitmaster */
NGX_MODULE_V1_PADDING
};
两个全局变量ngx_event_core_module_ctx和ngx_event_core_commands,两个函数指针ngx_event_module_init和ngx_event_process_init,如下图所示(下面的箭头表示包含关系):
这里,ngx_event_core_module所包含的全局变量ngx_event_core_commands(该变量类型为结构体structngx_command_s),它内部包含了一个函数指针ngx_event_use,如上图所示;
到现在为止,上文关注的全局变量ngx_event_core_module直接或间接包含的三个函数指针ngx_event_module_init、ngx_event_process_init和ngx_event_use都会对该全局变量进行操作。在nginx中只有6个函数操作了全局变量ngx_event_core_module:ngx_event_module_init、ngx_event_process_init、ngx_event_use、ngx_event_accept、ngx_poll_init_conf、ngx_select_init_conf,如下图所示:
ngx_event_core_module所包含的全局变量ngx_event_core_module_ctx的类型为结构体ngx_event_module_t,该结构体定义为:
typedef struct {
ngx_str_t *name;
void *(*create_conf)(ngx_cycle_t*cycle);
char *(*init_conf)(ngx_cycle_t*cycle, void *conf);
ngx_event_actions_t actions;
} ngx_event_module_t;
该结构体中的函数指针成员init_conf指向了初始化事件处理的操作函数,在全局变量ngx_event_core_module_ctx中该函数指针成员被赋值为函数:ngx_event_core_init_conf(见src\event\ngx_event.c)。nginx针对不同操作系统支持各种多路IO复用的方式,例如epoll、poll、kqueue、select,在该函数中,将根据OS的类型选择适用的事件及其处理函数,如果是epoll,则在编译时将会选择全局变量ngx_epoll_module,后文将以epoll为例描述如何以该全局变量为线索将epoll相关的操作串起来。如下图所示:
2. 全局变量ngx_event_actions
在前面的介绍中提到要重点关注宏ngx_process_events,在本节中将详细描述该宏如何适配不同的IO处理模型?不同OS所采用的IO多路复用技术的实现方式不同,并且每个IO处理模型都有自己独特的实现方式和接口,nginx采用如下方式来支持多种IO处理方式:
(1)首先将对各种不同的IO复用的处理方式进行抽象,每种处理方式都被抽象成一个函数指针,然后这些函数指针组成一个结构体,即:ngx_event_actions_t,结构体ngx_event_actions_t的定义为:
typedefstruct {
ngx_int_t (*add)(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags);
ngx_int_t (*del)(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags);
ngx_int_t (*enable)(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags);
ngx_int_t (*disable)(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags);
ngx_int_t (*add_conn)(ngx_connection_t *c);
ngx_int_t (*del_conn)(ngx_connection_t *c, ngx_uint_t flags);
ngx_int_t (*notify)(ngx_event_handler_pt handler);
ngx_int_t (*process_events)(ngx_cycle_t *cycle, ngx_msec_t timer,
ngx_uint_t flags);
ngx_int_t (*init)(ngx_cycle_t *cycle, ngx_msec_t timer);
void (*done)(ngx_cycle_t *cycle);
} ngx_event_actions_t;
(2)定义一个ngx_event_actions_t类型的全局变量ngx_event_actions用以保存实际的处理函数,例如:epoll、poll、kqueue等每种方式都会定义自己的处理函数;以epoll为例,在epoll的ngx_epoll_init函数中将所定义的epoll处理函数赋值给该全局变量ngx_event_actions,从而,我们前面重点关注的宏ngx_process_events也就被赋予了操作epoll的具体函数指针。
(3)在文件src\event\ngx_event.h中,又对ngx_event_actions中的各函数指针进行重新宏定义:
#definengx_process_events ngx_event_actions.process_events
#definengx_done_events ngx_event_actions.done
#definengx_add_event ngx_event_actions.add
#definengx_del_event ngx_event_actions.del
#definengx_add_conn ngx_event_actions.add_conn
#definengx_del_conn ngx_event_actions.del_conn
#definengx_notify ngx_event_actions.notify
而在nginx的代码中,都是使用这些宏来进行IO多路复用的操作,例如执行宏ngx_add_event,就是执行全局变量的add函数指针:ngx_event_actions.add,而全局变量ngx_event_actions又在初始化时被不同的IO多路复用方式赋值为各自的处理函数。我们前面重点关注的宏ngx_process_events就是在这里定义的。
3. 以epoll为例说明nginx如何进行网络事件处理?
Nginx支持多种网络处理方式,这里以常用的epoll为例说明其工作原理。根据前文所述,函数ngx_event_core_init_conf中调用了全局变量ngx_epoll_module,该结构体变量中将逐步引入其他epoll相关的变量或者函数,如下图所示:
在上图中,椭圆表示全局变量,方框表示函数或者函数指针;
(1)重点数据结构和全局变量
ngx_epoll_module变量的类型为结构体ngx_module_t,该结构体的定义为:
ngx_module_t ngx_epoll_module = {
NGX_MODULE_V1,
&ngx_epoll_module_ctx, /* module context */
ngx_epoll_commands, /* module directives */
NGX_EVENT_MODULE, /* module type */
NULL, /* init master*/
NULL, /* initmodule */
NULL, /* init process*/
NULL, /* init thread*/
NULL, /* exit thread*/
NULL, /* exit process */
NULL, /* exit master*/
NGX_MODULE_V1_PADDING
};
上述ngx_epoll_module结构体变量包含一个全局变量ngx_epoll_module_ctx,其类型为结构体ngx_event_module_t,该结构体定义为:
typedefstruct {
ngx_str_t *name;
void *(*create_conf)(ngx_cycle_t*cycle);
char *(*init_conf)(ngx_cycle_t*cycle, void *conf);
ngx_event_actions_t actions;
} ngx_event_module_t;
该结构体的ngx_event_actions_t类型的成员变量actions,保存了epoll对事件的所有相关处理函数,根据前文所述,这里保存的actions将被赋值给全局事件结构体变量ngx_event_actions。
在epoll的事件处理函数中上述结构体变量被进行了如下赋值(见文件src\event\modules\ngx_epoll_module.c):
ngx_event_module_t ngx_epoll_module_ctx = {
&epoll_name,
ngx_epoll_create_conf, /* create configuration */
ngx_epoll_init_conf, /* init configuration */
//下面用于设置epoll事件的所有处理函数到成员actions(类型为结构体ngx_event_actions_t)中
{
ngx_epoll_add_event, /* add an event */
ngx_epoll_del_event, /* delete an event */
ngx_epoll_add_event, /* enable an event */
ngx_epoll_del_event, /* disable an event */
ngx_epoll_add_connection, /* add an connection */
ngx_epoll_del_connection, /* delete an connection */
#if(NGX_HAVE_EVENTFD)
ngx_epoll_notify, /* trigger a notify */
#else
NULL, /* trigger a notify*/
#endif
ngx_epoll_process_events, /* process the events */
ngx_epoll_init, /* init the events */
ngx_epoll_done, /* done the events */
}
};