nginx进程模型
2017-05-26 14:55
489 查看
一、介绍
nginx的核心进程采用的是master-worker进程。一个master进程(监控进程)和多个worker进程(工作进程)。master进程:
1、fork出子进程(工作进程)。
2、接受来自外界的信号。
3、监控worker进程的工作状态,向worker进程发送信号,控制worker的运行状态。
4、当worker因某种原因退出时,及时启动worker进程。
5、监听端口,把监听句柄交给worker进程。
woker进程:
1、accept新的连接。
2、接收并处理数据。
3、接收来自master的进程控制。
二、进程控制(热重启)
nginx通过nginx -s 命令去控制进程的运行,nginx -s 是向master主进程发送命令,然后master主进程去控制子进程。nginx执行命令的时候,会获取到nginx的安装目录下的 nginx/logs/nginx.pid 文件,里面保存着master进程ID,然后通过kill 向 master发送进程控制命令。
具体的代码如下:
1、执行main函数(src/core/nginx.c)
int ngx_cdeclmain(int argc, char *const *argv)
{
...
//1、ngx_get_options获取命令参数并解析
if (ngx_get_options(argc, argv) != NGX_OK) {
return 1;
}
...
//2、执行信号处理函数ngx_signal_process,ngx_signal会在ngx_get_options赋值
if (ngx_signal) {
return ngx_signal_process(cycle, ngx_signal); //直接执行然后return
}
...
}
//ngx_get_options获取并解析命令
static ngx_int_t
ngx_get_options(int argc, char *const *argv)
{
...
case 's':
//ngx_signal 赋值
if (*p) {
ngx_signal = (char *) p;
} else if (argv[++i]) {
ngx_signal = argv[i];
} else {
ngx_log_stderr(0, "option \"-s\" requires parameter");
return NGX_ERROR;
}
//命令字符串对比
if (ngx_strcmp(ngx_signal, "stop") == 0
|| ngx_strcmp(ngx_signal, "quit") == 0
|| ngx_strcmp(ngx_signal, "reopen") == 0
|| ngx_strcmp(ngx_signal, "reload") == 0)
{
ngx_process = NGX_PROCESS_SIGNALLER;
//NGX_PROCESS_SIGNALLER宏定义为2,作用是为了标示nginx不是第一次启动而是nginx已经启动了,
//有些操作没有必须要重复去做,主要在ngx_cycle_init初始化的时候使用
goto next;
}
...
}
2、执行ngx_signal_process(),主要实现读取当前nginx的master进程pid。然后发送命令。
ngx_int_t ngx_signal_process(ngx_cycle_t *cycle, char *sig) { ... //读取log文件中的nginx.pid ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module); ngx_memzero(&file, sizeof(ngx_file_t)); file.name = ccf->pid; file.log = cycle->log; file.fd = ngx_open_file(file.name.data, NGX_FILE_RDONLY, NGX_FILE_OPEN, NGX_FILE_DEFAULT_ACCESS); if (file.fd == NGX_INVALID_FILE) { ngx_log_error(NGX_LOG_ERR, cycle->log, ngx_errno, ngx_open_file_n " \"%s\" failed", file.name.data); return 1; } n = ngx_read_file(&file, buf, NGX_INT64_LEN + 2, 0);//读取nginx.pid文件 if (ngx_close_file(file.fd) == NGX_FILE_ERROR) { ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, ngx_close_file_n " \"%s\" failed", file.name.data); } if (n == NGX_ERROR) { return 1; } while (n-- && (buf == CR || buf == LF)) { /* void */ } pid = ngx_atoi(buf, ++n); //获取进程ID if (pid == NGX_ERROR) { ngx_log_error(NGX_LOG_ERR, cycle->log, 0, "invalid PID number \"%*s\" in \"%s\"", n, buf, file.name.data); return 1; } //执行系统信号处理 return ngx_os_signal_process(cycle, sig, pid); }
ngx_int_t ngx_os_signal_process(ngx_cycle_t *cycle, char *name, ngx_int_t pid) { ngx_signal_t *sig; for (sig = signals; sig->signo != 0; sig++) { //遍历nginx命令与进程控制命令关系表 if (ngx_strcmp(name, sig->name) == 0) { if (kill(pid, sig->signo) != -1) { //发给master命令。signals在文件中有定义如下 return 0; } ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno, "kill(%P, %d) failed", pid, sig->signo); } } return 1; } //signals 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 }, { ngx_signal_value(NGX_NOACCEPT_SIGNAL), "SIG" ngx_value(NGX_NOACCEPT_SIGNAL), "", ngx_signal_handler }, { ngx_signal_value(NGX_TERMINATE_SIGNAL), "SIG" ngx_value(NGX_TERMINATE_SIGNAL), "stop", ngx_signal_handler }, { ngx_signal_value(NGX_SHUTDOWN_SIGNAL), "SIG" ngx_value(NGX_SHUTDOWN_SIGNAL), "quit", ngx_signal_handler }, { ngx_signal_value(NGX_CHANGEBIN_SIGNAL), "SIG" ngx_value(NGX_CHANGEBIN_SIGNAL), "", ngx_signal_handler }, { SIGALRM, "SIGALRM", "", ngx_signal_handler }, { SIGINT, "SIGINT", "", ngx_signal_handler }, { SIGIO, "SIGIO", "", ngx_signal_handler }, { SIGCHLD, "SIGCHLD", "", ngx_signal_handler }, { SIGSYS, "SIGSYS, SIG_IGN", "", SIG_IGN }, { SIGPIPE, "SIGPIPE, SIG_IGN", "", SIG_IGN }, { 0, NULL, "", NULL } };
至此执行nginx -s 的命令通过以上逻辑就发给了master主进程。下面看一下master主进程如何处理接受到的命令的。
void ngx_master 4000 _process_cycle(ngx_cycle_t *cycle) { ... //加入信号命令集 sigemptyset(&set); sigaddset(&set, SIGCHLD); sigaddset(&set, SIGALRM); sigaddset(&set, SIGIO); sigaddset(&set, SIGINT); sigaddset(&set, ngx_signal_value(NGX_RECONFIGURE_SIGNAL)); sigaddset(&set, ngx_signal_value(NGX_REOPEN_SIGNAL)); sigaddset(&set, ngx_signal_value(NGX_NOACCEPT_SIGNAL)); sigaddset(&set, ngx_signal_value(NGX_TERMINATE_SIGNAL)); sigaddset(&set, ngx_signal_value(NGX_SHUTDOWN_SIGNAL)); sigaddset(&set, ngx_signal_value(NGX_CHANGEBIN_SIGNAL)); ... //死循环等待信号 for ( ;; ) { ... //阻塞等待信号,接收到信号之后会执行绑定好的ngx_singal_hander函数 sigsuspend(&set); if (ngx_terminate) {...} //nginx -s stop if (ngx_quit) {...} //nginx -s quit if (ngx_restart) {...} //nginx -s restart if (ngx_reconfigure) {...} //nginx -s reload ... } }
master如何控制woker进程
master是通过管道单向给woker发送数据的。
1、先看一下channel的结构体
typedef struct { ngx_uint_t command; //发给worker的具体命令 ngx_pid_t pid; //woker的进程pid ngx_int_t slot; //woker在ngx_process数组的下标,所有的进程都会保存到ngx_process数组中。 ngx_fd_t fd; //文件描述,master有可能会把文件描述符告诉worker,让worker操作,先大致了解。 } ngx_channel_t;
2、master如何发送channel
static void ngx_signal_worker_processes(ngx_cycle_t *cycle, int signo) { //定义channel结构体 ngx_channel_t ch; ch.command = 0; switch (signo) { case ngx_signal_value(NGX_SHUTDOWN_SIGNAL): ch.command = NGX_CMD_QUIT; break; case ngx_signal_value(NGX_TERMINATE_SIGNAL): ch.command = NGX_CMD_TERMINATE; break; case ngx_signal_value(NGX_REOPEN_SIGNAL): ch.command = NGX_CMD_REOPEN; break; default: ch.command = 0; } ... if (ch.command) { //发送命令 ngx_write_channel 会调用sendMsg发送数据 if (ngx_write_channel(ngx_processes[i].channel[0], &ch, sizeof(ngx_channel_t), cycle->log) == NGX_OK) { if (signo != ngx_signal_value(NGX_REOPEN_SIGNAL)) { ngx_processes[i].exiting = 1; } continue; } } ... }
3、worker如何接收channel
worker会把channel加入epoll中,当有channel来的时候,会触发ngx_channel_handler函数。执行ngx_worker_process_init() 通过ngx_add_channel_event()把事件加入epoll,并绑定事件处理函数ngx_channel_handler。
ngx_channel_handler函数负责将一些变量置位,在woker的for(;;)中去读变量。
static void ngx_worker_process_init(ngx_cycle_t *cycle, ngx_int_t worker) { ... //加入epoll if (ngx_add_channel_event(cycle, ngx_channel, NGX_READ_EVENT, ngx_channel_handler) == NGX_ERROR) { exit(2); } ... } static void ngx_channel_handler(ngx_event_t *ev) { ... //置位相应的标志位 switch (ch.command) { case NGX_CMD_QUIT: ngx_quit = 1; break; case NGX_CMD_TERMINATE: ngx_terminate = 1; break; case NGX_CMD_REOPEN: ngx_reopen = 1; break; ... } static void ngx_worker_process_cycle(ngx_cycle_t *cycle, void *data) { ... //worker主循环,思路同master基本一样 for (;;) { if (ngx_terminate) {...} //nginx -s stop if (ngx_quit) {...} //nginx -s quit if (ngx_restart) {...} //nginx -s restart if (ngx_reconfigure) {...} //nginx -s reload } }
至此从发送一个nginx -s 命令后,nginx的master进程和worker进程都做了什么事情,以及master如何控制woker,大致原理就这些。后续如果有需要热重启的或者采用master-woker进程的,可以参考nginx的进程模型。
三、惊群现象(worker)
相关文章推荐
- 图解Nginx-Nginx进程模型2
- Nginx学习之六-nginx核心进程模型
- nginx多进程模型之配置热加载
- Nginx进程模型(一)master进程
- nginx多进程模型之热代码平滑升级
- Nginx的进程模型
- nginx源码分析--master和worker进程模型
- nginx 源码分析 进程模型
- nginx进程模型
- nginx进程模型
- nginx进程模型
- nginx核心进程模型
- Nginx源码学习(三) nginx进程模型
- Nginx学习之六-nginx核心进程模型
- nginx 多进程模型之总览
- nginx 之 多进程模型
- nginx源码分析--框架设计 & master-worker进程模型
- Nginx-进程模型
- Nginx 之三:nginx服务器模块、web请求处理机制及事件驱动模型、进程功能和进程间通信
- Nginx 进程模型及事件处理机制