nginx 进程模型
nginx的进程模型和大多数后台服务程序一样,按职责将进程分成监控进程和工作进程两类,启动nginx的主进程充当监控进程,而由主进程fork出来的子进程则充当工作进程。工作进程的任务自然是完成具体的业务逻辑,而监控进程充当整个进程组的对外接口,同时对工作进程进行监护,比如如果某工作进程意外退出,监控进程将重新fork生成一个新的工作进程。nginx也可以单进程模型执行,在这种进程模型下,主进程就是工作进程,此时没有监控进程,单进程模型比较简单且官方建议仅供测试使用,所以下面主要分析多进程模型。
nginx多进程模型的入口函数为主进程的 ngx_master_process_cycle(), 在该函数内做完信号处理,设置等之后就会调用 ngx_start_worker_processes(),用于fork()创建子进程(子进程的数目通过ccf->worker_processes指定),子进程作为一个新的实体,开始充当工作者的角色运行ngx_worker_process_cycle()函数, 该函数实体为一个无限的for循环,持续不断的处理客户端的服务请求,而主进程继续执行ngx_master_process_cycle(),也就是监控子进程的for循环,主进程也是一个无限for循环,直到进程退出,才会退出循环.
监控进程的无限for循环内的有一个关键的函数sigsuspend()函数调用,该函数的调用使得监控进程的大部分时间处于挂起等待状态,直到监控进程接收到信号为止,当监控进程接收到信号时,信号处理函数ngx_signal_handler()就会被执行.信号处理函数执行的主要动作就是根据当前信号值相对应的旗标变量做设置,而实际的处理逻辑必须放在主体代码里来处理,所以该for循环接下来的代码就是判断有哪些旗标变量被设置而需要处理的,比如ngx_reap(有子进程退出),ngx_quit或ngx_terminate(进程要退出或中止,2个旗标都是表示推出nginx,但ngx_quit更优雅,它会让nginx监控进程做一些清理工作且等待子进程也完全清理并退出之后才终止,而ngx_terminate更为粗暴,不过它通过使用SIGKILL信号能保证在一段时间后必定被结束掉),ngx_reconfigure(重新加载配置)等.当所有的信号都处理完以后又挂起在函数sigsuspend()调用处继续等待新的信号,如此反复.
构成监控进程的主要执行体
void ngx_master_process_cycle(ngx_cycle_t *cycle) { ..... //初始话子进程 ngx_start_worker_processes(cycle, ccf->worker_processes, NGX_PROCESS_RESPAWN); ngx_start_cache_manager_processes(cycle, 0); ..... for ( ; ; ) { if (delay) { } ..... sigsuspend(&set);//挂起主进程,等待信号 ..... if (ngx_reap) { //子进程退出 } ..... if (!live && (ngx_terminate || ngx_quit)) { } ....... if (ngx_terminate) { } //exit nginx ..... if (ngx_quit){ } ..... if (ngx_reconfigure) { } ..... if (ngx_restart) { } ..... if (ngx_reopen) { } }
信号的数据结构 typedef struct { int signo; //信号值 char *signame; char *name; void (*handler)(int signo); //信号的处理函数 } ngx_signal_t;
//信号数组 ngx_signal_t signals[] = { { ngx_signal_value(NGX_RECONFIGURE_SIGNAL), "SIG" ngx_value(NGX_RECONFIGURE_SIGNAL), "reload", ngx_signal_handler }, { ngx_signal_value(NGX_REOPEN_SIGNAL), "SIG" ngx_value(NGX_REOPEN_SIGNAL), "reopen", ngx_signal_handler }, ...... }
//获得信号后的处理函数 void ngx_signal_handler(int signo) { .... switch (ngx_process) { case NGX_PROCESS_MASTER: case NGX_PROCESS_SINGLE: switch (signo) { case ngx_signal_value(NGX_SHUTDOWN_SIGNAL): ngx_quit = 1; //将ngx_quit设置为1,将执行主进程中if(ngx_quit)下的程序 action = ", shutting down"; break; ..... } } }
工作进程的执行主题与监控进程类似,那么它的主要关注点就是与客户端或后端真实服务器(此时nginx为中间代理)之间的数据可读/可写等交互事件,而不是进程信号,所以工组进程的阻塞点在select,epoll等这样的I/O多路复用的函数调用处,以等待发生数据可读/可写事件,当然,也可能被新接收到的信号中断.
ngx_process_cycle.c中的工作进程的函数主体 :
static void ngx_worker_process_cycle(ngx_cycle_t *cycle, void *data) { ...... ngx_worker_process_init(cycle, 1); ...... for ( ;; ) { if (ngx_exiting) { } ......... ngx_process_events_and_timers(cycle); if (ngx_terminate) {} ........ if (ngx_quit) {} ........ if (ngx_reopen) {} }