nginx进程管理-signaller进程
2020年5月25日
nginx控制进程用来控制master进程,我们知道nginx有四种类型:
位于:src/os/unix/ngx_process_cycle.h
1 2 3 4 |
#define NGX_PROCESS_MASTER 1 #define NGX_PROCESS_SIGNALLER 2 #define NGX_PROCESS_WORKER 3 #define NGX_PROCESS_HELPER 4 |
当我们要对nginx升级、重启、切割等待日志时,这是nginx的角色为NGX_PROCESS_SIGNALLER
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
int ngx_cdecl main(int argc, char *const *argv) { .... if (ngx_get_options(argc, argv) != NGX_OK) { return 1; } .... cycle = ngx_init_cycle(&init_cycle); ... if (ngx_signal) { return ngx_signal_process(cycle, ngx_signal); } } static ngx_int_t ngx_get_options(int argc, char *const *argv) { ... for (i = 1; i < argc; i++) { ... while (*p) { switch (*p++) { ... case 's': 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; goto next; } ngx_log_stderr(0, "invalid option: \"-s %s\"", ngx_signal); return NGX_ERROR; ... } } ... return NGX_OK; } |
也就是说在我们使用nginx -s 信号时这时启动的nginx属于NGX_PROCESS_SIGNALLER类型了,做为signaller的nginx它的作用是对master的管理,信号类型存储在ngx_signal这个变量里,它的用法是这样的:
1 2 3 4 5 6 7 |
nginx -sXXX 或者 nginx -s XXX XXX可选为: stop, 立即退出 quit, 平滑退出 reopen, 重新打开所有文件 reload, 重新加载配置 |
当程序启动时我们指定了合法的-s参数时,ngx_process被设置成了NGX_PROCESS_SIGNALLER,接下来的初始化函数ngx_init_cycle中,识别了NGX_PROCESS_SIGNALLER这个类型会把创建pid,创建文件,打开端口等等这些全部忽略,只调用了解析配置和各个模块的初始化配置的回调函数,所以我们在写core模块时一定要注意不要在ini_conf回调函数打开资源。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
ngx_cycle_t * ngx_init_cycle(ngx_cycle_t *old_cycle) { .... for (i = 0; cycle->modules[i]; i++) { if (cycle->modules[i]->type != NGX_CORE_MODULE) { continue; } ... if (module->create_conf) { rv = module->create_conf(cycle); if (rv == NULL) { ngx_destroy_pool(pool); return NULL; } cycle->conf_ctx[cycle->modules[i]->index] = rv; } } .... if (ngx_conf_parse(&conf, &cycle->conf_file) != NGX_CONF_OK) { ... } ... for (i = 0; cycle->modules[i]; i++) { if (cycle->modules[i]->type != NGX_CORE_MODULE) { continue; } module = cycle->modules[i]->ctx; if (module->init_conf) { if (module->init_conf(cycle, cycle->conf_ctx[cycle->modules[i]->index]) == NGX_CONF_ERROR) { ... } } } if (ngx_process == NGX_PROCESS_SIGNALLER) { /*signaller process*/ return cycle; } ... if (ngx_test_lockfile(cycle->lock_file.data, log) != NGX_OK) { goto failed; } if (ngx_create_paths(cycle, ccf->user) != NGX_OK) { goto failed; } if (ngx_log_open_default(cycle) != NGX_OK) { goto failed; } /* open the new files */ ... /* create shared memory */ ... /* handle the listening sockets */ ... } |
我们可以看到在初始化时如果是信号进程会跳过一些系申请统资源之类的操作,同时也会初始化最外层的core模块的配置。在所有配置解析并初始化完成后,信号进程开始给master发送指令了,这过程就是读取配置后的pid文件里的进程ID,然后通过进程ID给master进行发送信号了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
ngx_int_t ngx_signal_process(ngx_cycle_t *cycle, char *sig) { ... 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); ... n = ngx_read_file(&file, buf, NGX_INT64_LEN + 2, 0); ... while (n-- && (buf[n] == CR || buf[n] == LF)) { /* void */ } pid = ngx_atoi(buf, ++n); ... return ngx_os_signal_process(cycle, sig, pid); } ngx_int_t ngx_os_signal_process(ngx_cycle_t *cycle, char *name, ngx_pid_t pid) { ... for (sig = signals; sig->signo != 0; sig++) { if (ngx_strcmp(name, sig->name) == 0) { if (kill(pid, sig->signo) != -1) { return 0; } .. } } return 1; } ngx_signal_t signals[] = { { ngx_signal_value(NGX_RECONFIGURE_SIGNAL), "SIG" ngx_value(NGX_RECONFIGURE_SIGNAL),/*SIGHUP*/ "reload", ngx_signal_handler }, { ngx_signal_value(NGX_REOPEN_SIGNAL), "SIG" ngx_value(NGX_REOPEN_SIGNAL),/*SIGUSR1*/ "reopen", ngx_signal_handler }, ... { ngx_signal_value(NGX_TERMINATE_SIGNAL),/*SIGTERM*/ "SIG" ngx_value(NGX_TERMINATE_SIGNAL), "stop", ngx_signal_handler }, { ngx_signal_value(NGX_SHUTDOWN_SIGNAL),/*SIGQUIT*/ "SIG" ngx_value(NGX_SHUTDOWN_SIGNAL), "quit", ngx_signal_handler }, ... }; #define NGX_SHUTDOWN_SIGNAL QUIT #define NGX_TERMINATE_SIGNAL TERM #define NGX_NOACCEPT_SIGNAL WINCH #define NGX_RECONFIGURE_SIGNAL HUP #define NGX_REOPEN_SIGNAL USR1 #define NGX_CHANGEBIN_SIGNAL USR2 |
如果只是发送信号,从代码里了解到下面是等价的:(假设master的进程号是12345)
1 2 3 4 |
nginx -s stop <=> kill -term 12345 nginx -s quit <=> kill -quit 12345 nginx -s reopen <=> kill -usr1 12345 nginx -s reload <=> kill -hup 12345 |
至于为什么要用这些信号呢或者大家可以思考一下,这两种方法的利弊在什么地方。