在看nginx源码的时候,遇到这样一个函数:ngx_add_inherited_sockets, 这个函数在main函数里被调用。
里面具体做的事情就是:从一个环境变量读出socket文件描述符,然后这个socket文件描述符就可以用了,不用调用socket()函数创建。具体可以看这位大神的博客:http://blog.csdn.net/livelylittlefish/article/details/7277607
但我读完这篇博客还是有一个困惑的问题,可能是我智商捉急不能理解大神的文章:为什么socket从环境变量里读出来,就能用了?
进过反复阅读代码终于懂了,这就是nginx热启动。
来看一下这个热启动的执行过程。
1:给nginx发一个USR2信号:在ngx_process.c文件里有一个signals信号数组,这个数组定义了nginx所支持的信号,
其中一个,
{ ngx_signal_value(NGX_CHANGEBIN_SIGNAL),
"SIG" ngx_value(NGX_CHANGEBIN_SIGNAL),
"",
ngx_signal_handler },
ngx_config.h里面有
#define NGX_CHANGEBIN_SIGNAL USR2
2:处理函数ngx_signal_hanler做了啥:
ngx_process.c:
case ngx_signal_value(NGX_CHANGEBIN_SIGNAL):
if (getppid() > 1 || ngx_new_binary > 0) {
/*
* Ignore the signal in the new binary if its parent is
* not the init process, i.e. the old binary's process
* is still running. Or ignore the signal in the old binary's
* process if the new binary's process is already running.
*/
/* 这里给出了详细的注释,更通俗一点来讲,就是说,进程现在是一个
* master(新的master进程),但是当他的父进程old master还在运行的话,
* 这时收到了USR2信号,我们就忽略它,不然就成了新master里又要生成
* master。。。另外一种情况就是,old master已经开始了生成新master的过程
* 中,这时如果又有USR2信号到来,那么也要忽略掉。。。(不知道够不够通俗=.=)
参考文档:http://blog.csdn.net/dingyujie/article/details/7192144
*/
action = ", ignoring";
ignore = 1;
break;
}
//正常情况下,需要热代码替换,设置标志位
ngx_change_binary = 1;
action = ", changing binary";
break;
里面设置了ngx_change_binary = 1,这一句是设置热启动标志
3:ngx_process_cycle.c里面有个 ngx_master_process_cycle 这里面会循环检测各种标志,其中就包括热启动
//热代码替换
if (ngx_change_binary) { // 信号处理函数里将这个设置成了1
ngx_change_binary = 0;
ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "changing binary");
//进行热代码替换,这里是调用execve来执行新的代码
ngx_new_binary = ngx_exec_new_binary(cycle, ngx_argv);
}
4:ngx_exec_new_binary干了啥?
1:现将socket写到环境变量里面
2:pid = ngx_execute(cycle, &ctx)
5:ngx_execute干了啥?
ngx_pid_t
ngx_execute(ngx_cycle_t *cycle, ngx_exec_ctx_t *ctx)
{
return ngx_spawn_process(cycle, ngx_execute_proc, ctx, ctx->name,
NGX_PROCESS_DETACHED);
}
6: ngx_spawn_process干了啥?
ngx_spawn_process(ngx_cycle_t *cycle, ngx_spawn_proc_pt proc, void *data,
char *name, ngx_int_t respawn);
这个函数创建子进程,并让子进程执行proc函数,第五步传进来的是ngx_execute_proc
ngx_spawn_process函数里有://创建子进程
pid = fork();
switch (pid) {
case -1:
ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
"fork() failed while spawning \"%s\"", name);
ngx_close_channel(ngx_processes[s].channel, cycle->log);
return NGX_INVALID_PID;
case 0:
ngx_pid = ngx_getpid();
//在子进程中执行传递进来的函数,即工作进程的具体工作
proc(cycle, data);
break;
default:
break;
}
7:ngx_execute_proc做了啥:
static void
ngx_execute_proc(ngx_cycle_t *cycle, void *data)
{
ngx_exec_ctx_t *ctx = data;
if (execve(ctx->path, ctx->argv, ctx->envp) == -1) {
ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
"execve() failed while executing %s \"%s\"",
ctx->name, ctx->path);
}
exit(1);
}
这个函数里调用的execve
而文件描述符只要不设置close_on_exec就可以被继承下来。
说到这里,我感觉原来的我已经懂了。
里面具体做的事情就是:从一个环境变量读出socket文件描述符,然后这个socket文件描述符就可以用了,不用调用socket()函数创建。具体可以看这位大神的博客:http://blog.csdn.net/livelylittlefish/article/details/7277607
但我读完这篇博客还是有一个困惑的问题,可能是我智商捉急不能理解大神的文章:为什么socket从环境变量里读出来,就能用了?
进过反复阅读代码终于懂了,这就是nginx热启动。
来看一下这个热启动的执行过程。
1:给nginx发一个USR2信号:在ngx_process.c文件里有一个signals信号数组,这个数组定义了nginx所支持的信号,
其中一个,
{ ngx_signal_value(NGX_CHANGEBIN_SIGNAL),
"SIG" ngx_value(NGX_CHANGEBIN_SIGNAL),
"",
ngx_signal_handler },
ngx_config.h里面有
#define NGX_CHANGEBIN_SIGNAL USR2
2:处理函数ngx_signal_hanler做了啥:
ngx_process.c:
case ngx_signal_value(NGX_CHANGEBIN_SIGNAL):
if (getppid() > 1 || ngx_new_binary > 0) {
/*
* Ignore the signal in the new binary if its parent is
* not the init process, i.e. the old binary's process
* is still running. Or ignore the signal in the old binary's
* process if the new binary's process is already running.
*/
/* 这里给出了详细的注释,更通俗一点来讲,就是说,进程现在是一个
* master(新的master进程),但是当他的父进程old master还在运行的话,
* 这时收到了USR2信号,我们就忽略它,不然就成了新master里又要生成
* master。。。另外一种情况就是,old master已经开始了生成新master的过程
* 中,这时如果又有USR2信号到来,那么也要忽略掉。。。(不知道够不够通俗=.=)
参考文档:http://blog.csdn.net/dingyujie/article/details/7192144
*/
action = ", ignoring";
ignore = 1;
break;
}
//正常情况下,需要热代码替换,设置标志位
ngx_change_binary = 1;
action = ", changing binary";
break;
里面设置了ngx_change_binary = 1,这一句是设置热启动标志
3:ngx_process_cycle.c里面有个 ngx_master_process_cycle 这里面会循环检测各种标志,其中就包括热启动
//热代码替换
if (ngx_change_binary) { // 信号处理函数里将这个设置成了1
ngx_change_binary = 0;
ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "changing binary");
//进行热代码替换,这里是调用execve来执行新的代码
ngx_new_binary = ngx_exec_new_binary(cycle, ngx_argv);
}
4:ngx_exec_new_binary干了啥?
1:现将socket写到环境变量里面
2:pid = ngx_execute(cycle, &ctx)
5:ngx_execute干了啥?
ngx_pid_t
ngx_execute(ngx_cycle_t *cycle, ngx_exec_ctx_t *ctx)
{
return ngx_spawn_process(cycle, ngx_execute_proc, ctx, ctx->name,
NGX_PROCESS_DETACHED);
}
6: ngx_spawn_process干了啥?
ngx_spawn_process(ngx_cycle_t *cycle, ngx_spawn_proc_pt proc, void *data,
char *name, ngx_int_t respawn);
这个函数创建子进程,并让子进程执行proc函数,第五步传进来的是ngx_execute_proc
ngx_spawn_process函数里有://创建子进程
pid = fork();
switch (pid) {
case -1:
ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
"fork() failed while spawning \"%s\"", name);
ngx_close_channel(ngx_processes[s].channel, cycle->log);
return NGX_INVALID_PID;
case 0:
ngx_pid = ngx_getpid();
//在子进程中执行传递进来的函数,即工作进程的具体工作
proc(cycle, data);
break;
default:
break;
}
7:ngx_execute_proc做了啥:
static void
ngx_execute_proc(ngx_cycle_t *cycle, void *data)
{
ngx_exec_ctx_t *ctx = data;
if (execve(ctx->path, ctx->argv, ctx->envp) == -1) {
ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
"execve() failed while executing %s \"%s\"",
ctx->name, ctx->path);
}
exit(1);
}
这个函数里调用的execve
而文件描述符只要不设置close_on_exec就可以被继承下来。
说到这里,我感觉原来的我已经懂了。