FastCGI Process Manager (FPM) is a process manager for PHP's FastCGI execution mode. Its core functionality involves managing worker processes that handle FastCGI requests.
FastCGI Protocol Fundamentals
FastCGI is a application-layer communication protocol between web servers (like Nginx or Apache) and application processors. PHP implements the FastCGI protocol for request parsing but delegates network handling to the web server.
Process Management Models
PHP-FPM employs a multi-process model where a master process manages worker pools. Each worker pool listens on a designated socket and contains multiple worker processes. Unlike event-driven models (like Nginx's), an FPM worker process handles one request at a time in a blocking manner before accepting the next.
Configuration and Process Structure
Worker pools are defined in php-fpm.conf using [pool] sections:
[web1]
listen = 127.0.0.1:9000
[web2]
listen = 127.0.0.1:9001
The internal structure for a worker pool:
struct worker_pool {
struct worker_pool *next_pool;
struct pool_config *settings;
int socket_descriptor;
struct worker_process *worker_list;
int active_workers;
struct scoreboard *performance_data;
// ... additional fields
}
Initialization Process
The FPM startup sequence in fpm_main.c:
int main(int argc, char *argv[]) {
sapi_startup(&cgi_sapi_module);
if (cgi_sapi_module.startup(&cgi_sapi_module) == FAILURE) {
return FPM_EXIT_SOFTWARE;
}
fpm_init();
fpm_is_running = 1;
int fastcgi_socket = fpm_run(&max_requests);
// Worker process execution continues here
return process_requests(fastcgi_socket);
}
The fpm_init() function performs critical setup:
- Configuration parsing
- Shared memory allocation for scoreboarding
- Signal handler registration
- Socket creation for each pool
- Event system initialization
Request Processing Lifecycle
Worker processes follow this sequence:
- Request Acceptance: Block on
fcgi_accept_request() - Request Parsing: Parse FastCGI protocol headers and content
- Request Initialization: Execute
php_request_startup(), triggering extensionPHP_RINIT_FUNCTION()calls - Script Execution: Compile and execute PHP code via
php_execute_script() - Request Completion: Execute
php_request_shutdown(), triggering extensionPHP_RSHUTDOWN_FUNCTION()calls
Processing stages are tracked in the scoreboard:
FPM_REQUEST_ACCEPTING: Waiting for requestFPM_REQUEST_READING_HEADERS: Reading FastCGI headersFPM_REQUEST_INFO: Processing request metadataFPM_REQUEST_EXECUTING: Executing PHP scriptFPM_REQUEST_FINISHED: Request comlpeted
Process Management Strategies
FPM supports three process management approaches:
Static Pool Management
Worker count remains fixed at pm.max_children. No dynamic scaling occurs.
Dynamic Pool Management
Workers scale between pm.min_spare_servers and pm.max_spare_servers boundaries, with total not exceeding pm.max_children.
On-Demand Pool Management
Workers spawn only when requests arrive, up to pm.max_children. Idle workers terminate after pm.process_idle_timeout.
Master Process Event Loop
The master process enters an event loop after initialization:
void fpm_event_loop(int error_code) {
// Setup signal handling via pipe
fpm_event_set(&signal_event, signal_pipe[0],
FPM_EV_READ, &handle_signal, NULL);
fpm_event_add(&signal_event, 0);
// Configure heartbeat for request timeout monitoring
if (fpm_globals.heartbeat_interval > 0) {
setup_heartbeat_timer();
}
// Regular process maintenance
schedule_process_maintenance();
while (1) {
process_events();
}
}
Signal Handling Mechanism
Master process handles several signals through a pipe notification system:
SIGTERM/SIGINT/SIGQUIT: Graceful shutdownSIGUSR1: Log file rotationSIGUSR2: Process restartSIGCHLD: Worker process termination cleanup
Process Maintenance Timer
A periodic timer (approximately 1 second) manages dynamic and on-demand pools:
static void maintain_worker_pools(struct timeval *current_time) {
for (pool = all_pools; pool; pool = pool->next_pool) {
int idle_count = 0;
int active_count = 0;
struct worker *oldest_idle = NULL;
// Count idle and active workers
for (worker = pool->worker_list; worker; worker = worker->next) {
if (worker_is_idle(worker)) {
idle_count++;
if (!oldest_idle || worker->idle_start < oldest_idle->idle_start) {
oldest_idle = worker;
}
} else {
active_count++;
}
}
// On-demand pool management
if (pool->config->management_mode == ON_DEMAND && oldest_idle) {
if (oldest_idle->idle_duration > pool->config->idle_timeout) {
terminate_worker(oldest_idle->process_id);
}
continue;
}
// Dynamic pool management
if (pool->config->management_mode == DYNAMIC) {
if (idle_count > pool->config->max_spare_workers && oldest_idle) {
terminate_worker(oldest_idle->process_id);
}
if (idle_count < pool->config->min_spare_workers &&
pool->total_workers < pool->config->max_workers) {
spawn_new_worker(pool);
}
}
}
}
Request Timeout Enforcement
When request_terminate_timeout is configured, the master monitors request duration:
static void check_request_timeouts(struct timeval *current_time) {
for (pool = all_pools; pool; pool = pool->next_pool) {
int terminate_limit = pool->config->request_terminate_timeout;
int slow_log_limit = pool->config->request_slowlog_timeout;
if (terminate_limit || slow_log_limit) {
for (worker = pool->worker_list; worker; worker = worker->next) {
monitor_worker_timeout(worker, current_time,
terminate_limit, slow_log_limit);
}
}
}
}
Worker processes exceeding the timeout receive termination signals, and slow requests are logged when exceeding request_slowlog_timeout.
Inter-Process Communication
The master process monitors worker status through shared scoreboard memory rather than direct communication. Worker termination commands are sent via signals.