Understanding Nginx Process Management: Signals, Master‑Worker Loops, and Worker Initialization
This article explains how Nginx creates and controls its master and worker processes using Linux signals, global flag variables, and a series of initialization steps—including signal handling, process spawning, socketpair communication, and worker loop execution—to achieve robust, high‑concurrency request handling.
The article continues the walkthrough of Nginx startup by focusing on how the master process creates and manages worker processes, how signals are handled, and how global flag variables drive the behavior of both master and worker loops.
1. Signals and Global Flags
Nginx redefines handlers for several Linux signals (e.g., SIGCHLD, SIGTERM, SIGINT, SIGHUP, USR1, USR2, WINCH) via ngx_signal_handler . Each signal sets a corresponding global flag such as ngx_quit , ngx_terminate , ngx_reconfigure , etc., which the master and worker processes poll in their main loops to decide actions like graceful shutdown, reload, or binary upgrade.
static void ngx_signal_handler(int signo, siginfo_t *siginfo, void *ucontext) {
…
switch (ngx_process) {
case NGX_PROCESS_MASTER:
case NGX_PROCESS_SINGLE:
switch (signo) {
case ngx_signal_value(NGX_SHUTDOWN_SIGNAL):
ngx_quit = 1;
break;
case ngx_signal_value(NGX_TERMINATE_SIGNAL):
case SIGINT:
ngx_terminate = 1;
break;
case ngx_signal_value(NGX_NOACCEPT_SIGNAL):
if (ngx_daemonized) {
ngx_noaccept = 1;
action = ", stop accepting connections";
}
break;
case ngx_signal_value(NGX_RECONFIGURE_SIGNAL):
ngx_reconfigure = 1;
break;
case ngx_signal_value(NGX_REOPEN_SIGNAL):
ngx_reopen = 1;
break;
case ngx_signal_value(NGX_CHANGEBIN_SIGNAL):
ngx_change_binary = 1;
break;
case SIGALRM:
ngx_sigalrm = 1;
break;
case SIGIO:
ngx_sigio = 1;
break;
case SIGCHLD:
ngx_reap = 1;
break;
}
break;
…
}
}2. Master Process Work Cycle
The master process runs ngx_master_process_cycle , which first masks signals with sigprocmask , then starts workers, cache manager, and privileged agent processes. Workers are launched via ngx_start_worker_processes , which internally calls ngx_spawn_process (a wrapper around fork ) and sets up a socketpair for parent‑child communication.
ngx_pid_t ngx_spawn_process(ngx_cycle_t *cycle, ngx_spawn_proc_pt proc, void *data,
char *name, ngx_int_t respawn) {
u_long on;
ngx_pid_t pid;
ngx_int_t s;
…
if (respawn >= 0) {
s = respawn;
} else {
for (s = 0; s < ngx_last_process; s++) {
if (ngx_processes[s].pid == -1) {
break;
}
}
if (s == NGX_MAX_PROCESSES) {
ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,
"no more than %d processes can be spawned",
NGX_MAX_PROCESSES);
return NGX_INVALID_PID;
}
}
if (respawn != NGX_PROCESS_DETACHED) {
if (socketpair(AF_UNIX, SOCK_STREAM, 0, ngx_processes[s].channel) == -1) {
ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
"socketpair() failed while spawning \"%s\"", name);
return NGX_INVALID_PID;
}
// set non‑blocking, async, close‑on‑exec flags …
}
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_parent = ngx_pid;
ngx_pid = ngx_getpid();
proc(cycle, data);
break;
default:
break;
}
return pid;
}The ngx_process_t structure stores each child’s PID, status, communication channel, and flags such as respawn , just_spawn , detached , exiting , and exited :
typedef struct {
ngx_pid_t pid; // process pid
int status; // status from waitpid
ngx_socket_t channel[2]; // parent‑child sockets
ngx_spawn_proc_pt proc; // child work routine
char *name; // process name
unsigned respawn:1; // need to respawn
unsigned just_spawn:1; // currently spawning
unsigned detached:1; // detached from parent
unsigned exiting:1; // exiting flag
unsigned exited:1; // exited flag
} ngx_process_t;3. Worker Process Work Cycle
Workers execute ngx_worker_process_cycle . They first run ngx_worker_process_init to set environment, CPU affinity, and resource limits, then repeatedly check global flags ( ngx_quit , ngx_terminate , ngx_exiting ) to decide whether to shut down gracefully or immediately. The core of the worker is ngx_process_events_and_timers , which handles network events, timers, and post‑events using Nginx’s event‑driven architecture.
4. Overall Startup Flow
The complete startup consists of two phases: (1) creation and initialization of the core ngx_cycle_t structure, which parses configuration files; (2) spawning the master process and its workers, then entering their respective loops. The master monitors signals and global flags, while workers handle actual client connections.
5. Summary
This chapter provides a concise overview of Nginx’s initialization, signal handling, master‑worker process creation, and the key flag‑driven loops that enable graceful reloads, hot upgrades, and robust high‑concurrency request processing.
Xueersi Online School Tech Team
The Xueersi Online School Tech Team, dedicated to innovating and promoting internet education technology.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.