In the production environment, nginx generally adopts the model of one master process and multiple worker processes, in which the master process does not need to deal with network events, it is not responsible for the execution of business, but only manages the worker process to achieve functions such as restarting services, smooth upgrades, replacing log files, and configuration files taking effect in real time, while the worker process is used to provide services, such as static file services, reverse ** and other functions.
So how does nginx implement master restart service, smooth upgrade, replacement of log files, and configuration files take effect in real time?In addition, how does the master process inform the worker process of restarting the service, smoothly upgrading, replacing log files, and configurable files taking effect in real time?The answer is signals. Let's take a look at how nginx does the above in combination with **.
Because nginx uses signals to achieve smooth upgrades, replacement of log files, real-time effect of configuration files, restart services and other functions, the signals used in the startup process of nginx will be registered with the operating system kernel, and its ** implementation is as follows:
int ngx_cdeclngx init signals() is implemented as follows:main(int argc, char *const *ar**)
Initialization signal*
if (ngx_init_signals(cycle->log) != ngx_ok)
ngx_int_tFrom the implementation of the ngx init signals() function, we can see that nginx registers the supported signals to the operating system kernel by calling sigaction(), and when the kernel captures the corresponding signal, it will call the ** function for signal processing. As you can see from **, in fact, the corresponding handler for all signals supported by nginx is the same, that is, ngx signal hanlder(). In this function, a global variable corresponding to the signal in nginx will be set according to the signal of the lock, and then the corresponding action will be performed according to this global variable in the processing loop of the master process, which will be described later.ngx_init_signals(ngx_log_t *log)
ngx_signal_t *sig;
struct sigaction sa;Signals used by the Linux kernel.
Iterate through the signals array to register all nginx-supported signals* with the kernel
for (sig = signals; sig->signo != 0; sig++)
return ngx_ok;
Generally speaking, when the nginx process (including the master process and the worker process) is already running in the environment, the so-called smooth upgrade, replacement of log files or configuration files take effect in real time, and other management functions. So how does nginx control these features from the command line?The nginx approach is to start a new nginx process to send the corresponding control signals to the master process that already exists in the environment, so that the existing service can perform the corresponding action. So how does the new nginx process signal to the master process already in the environment?Its ** implementation is as follows:
int ngx_cdeclAs you can see from the implementation, the new nginx process returns to exit after executing this, because this newly started process is used to send signals. So how does the new process signal to the existing master process?The answer is via the kill system call. In general, there are two steps: the first is to get the running master process stored in nginxThe pid in the pid file, which is what the ngx signal process() function does, is implemented as follows:main(int argc, char *const *ar**)
"nginx -s xxx"*/
if (ngx_signal)
ngx_int_tSecondly, after obtaining the pid of the running master process, call the kill command to send the signal carried by the new nginx process to the running master process, which is also the function of the ngx os signal process() function, which is implemented as follows:ngx_signal_process(ngx_cycle_t *cycle, char *sig)
ssize_t n;
ngx_pid_t pid;
ngx_file_t file;
ngx_core_conf_t *ccf;
u_char buf[ngx_int64_len + 2];
Obtain the structure pointer of the configuration item stored in the core module*
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;CCF->PID is nginxpid file.
file.log = cycle->log;
Open nginx. in a readable mannerpid file.
file.fd = ngx_open_file(file.name.data, ngx_file_rdonly,ngx_file_open, ngx_file_default_access);
Read file * n = ngx read file(&file, buf, ngx int64 len + 2, 0);
if (ngx_close_file(file.fd) == ngx_file_error)
if (n == ngx_error)
Remove the ending control character*
while (n-- buf[n] == cr ||buf[n] == lf))
Convert the string to a number to get the master process pid*
pid = ngx_atoi(buf, +n);
Encapsulates the signaling function of the kill system call*
return ngx_os_signal_process(cycle, sig, pid);
ngx_int_tAt this point, the task of the new nginx process is completed, and then it returns and exits, so what happens to the running master process next?This involves the work loop of the master process. We know that the master process does not provide services to the outside world, but is specially used to manage the worker process, so how is it implemented in nginx?ngx_os_signal_process(ngx_cycle_t *cycle, char *name, ngx_pid_t pid)
ngx_signal_t *sig;
Traverse through the signals supported by the nginx kernel to find the same signal as the name, and go through the kill system.
The call sends a signal to the master process.
for (sig = signals; sig->signo != 0; sig++)
ngx_log_error(ngx_log_alert, cycle->log, ngx_errno,"kill(%p, %d) failed", pid, sig->signo);
return 1;
As mentioned earlier, nginx sends a signal to the master process by starting a new process, so the master process is waiting for the signal to arrive in the work loop. After the signal arrives, the signal processing function will be triggered, and then the corresponding flag position of the signal will be detected, and then the corresponding flag will be detected in the master process for corresponding processing. Before going into how the master handles specific signals, let's look at which signals the master process is interested in, as follows:
The chld signal in the above ** is not sent by a new nginx process, but the operating system kernel will send a chld signal to the parent process, i.e., the master, when it detects that a child process is exiting, and then the master process will further analyze this, which is the function of ngx reap children(). In ngx master process cycle() we can see that the master first adds the signal it is interested in to the set of signals that block itself (by calling sigprocmask() via sig block) and then by calling sigsuspend() after these actions Suspend yourself, wait for the signal in the signal set to occur, wake yourself up, and then do the corresponding processing according to the global variable (see the above table) after the signal occurs, and the processing flow chart is as follows:
From the work loop of the master process, we can see that when the master receives the corresponding signal and completes its own processing process, it will send the corresponding signal to the worker process through ngx signal worker processes(). For example, after receiving a quit signal, the master will send a quit signal to all worker sub-processes to inform the worker to exit gracefully (the so-called elegance is actually to process the existing connection and not accept new connections), and close the socket handle for the listener. So what signals will the worker process be interested in, and what will happen to it when it receives the corresponding signals from the master?This is what the worker work cycle is about. In the worker process, after receiving the signals sent by the master, we will also see which signals the worker is interested in and will set the corresponding global variables before introducing the worker work cycle, as follows:
In addition to the three signals described above, there is another global variable NGX exciting that can be seen in the worker process's work loop. There is only one place where this flag will be set, that is, after receiving a quit signal. NGX Quit will only set NGX Exciting to 1 for the first time. Why?Because when the worker receives the quit signal, it knows that it needs to gracefully shut down the process, i.e., finish processing existing connections and no longer accept new ones, NGX Exciting represents an exiting state, i.e., there are still connections that have not yet been processed. Here's how the worker process works:
This is just a brief description of the signal processing process of the master process and the worker process, for the detailed processing process, such as smooth upgrade, real-time effect of the configuration file, etc., you still need to read ** to better sort out the details.