php-internal-docs 8.4.8
Unofficial docs for php/php-src
Loading...
Searching...
No Matches
fpm_process_ctl.c
Go to the documentation of this file.
1 /* (c) 2007,2008 Andrei Nigmatulin */
2
3#include "fpm_config.h"
4
5#include <sys/types.h>
6#include <signal.h>
7#include <unistd.h>
8#include <stdlib.h>
9
10#include "fpm.h"
11#include "fpm_clock.h"
12#include "fpm_children.h"
13#include "fpm_signals.h"
14#include "fpm_events.h"
15#include "fpm_process_ctl.h"
16#include "fpm_cleanup.h"
17#include "fpm_request.h"
18#include "fpm_worker_pool.h"
19#include "fpm_scoreboard.h"
20#include "fpm_sockets.h"
21#include "fpm_stdio.h"
22#include "zlog.h"
23
24
25static int fpm_state = FPM_PCTL_STATE_NORMAL;
26static int fpm_signal_sent = 0;
27
28
29static const char *fpm_state_names[] = {
30 [FPM_PCTL_STATE_NORMAL] = "normal",
31 [FPM_PCTL_STATE_RELOADING] = "reloading",
32 [FPM_PCTL_STATE_TERMINATING] = "terminating",
33 [FPM_PCTL_STATE_FINISHING] = "finishing"
34};
35
36static int saved_argc;
37static char **saved_argv;
38
39static void fpm_pctl_cleanup(int which, void *arg) /* {{{ */
40{
41 int i;
42 if (which != FPM_CLEANUP_PARENT_EXEC) {
43 for (i = 0; i < saved_argc; i++) {
44 free(saved_argv[i]);
45 }
46 free(saved_argv);
47 }
48}
49/* }}} */
50
51static struct fpm_event_s pctl_event;
52
53static void fpm_pctl_action(struct fpm_event_s *ev, short which, void *arg) /* {{{ */
54{
56}
57/* }}} */
58
59static int fpm_pctl_timeout_set(int sec) /* {{{ */
60{
61 fpm_event_set_timer(&pctl_event, 0, &fpm_pctl_action, NULL);
62 fpm_event_add(&pctl_event, sec * 1000);
63 return 0;
64}
65/* }}} */
66
67static void fpm_pctl_exit(void)
68{
69 zlog(ZLOG_NOTICE, "exiting, bye-bye!");
70
74}
75
76#define optional_arg(c) (saved_argc > c ? ", \"" : ""), (saved_argc > c ? saved_argv[c] : ""), (saved_argc > c ? "\"" : "")
77
78static void fpm_pctl_exec(void)
79{
80 zlog(ZLOG_DEBUG, "Blocking some signals before reexec");
81 if (0 > fpm_signals_block()) {
82 zlog(ZLOG_WARNING, "concurrent reloads may be unstable");
83 }
84
85 zlog(ZLOG_NOTICE, "reloading: execvp(\"%s\", {\"%s\""
86 "%s%s%s" "%s%s%s" "%s%s%s" "%s%s%s" "%s%s%s"
87 "%s%s%s" "%s%s%s" "%s%s%s" "%s%s%s" "%s%s%s"
88 "})",
89 saved_argv[0], saved_argv[0],
90 optional_arg(1),
91 optional_arg(2),
92 optional_arg(3),
93 optional_arg(4),
94 optional_arg(5),
95 optional_arg(6),
96 optional_arg(7),
97 optional_arg(8),
98 optional_arg(9),
99 optional_arg(10)
100 );
101
103
105
106 execvp(saved_argv[0], saved_argv);
107 zlog(ZLOG_SYSERROR, "failed to reload: execvp() failed");
109}
110
111static void fpm_pctl_action_last(void)
112{
113 switch (fpm_state) {
115 fpm_pctl_exec();
116 break;
117
120 fpm_pctl_exit();
121 break;
122 }
123}
124
125int fpm_pctl_kill(pid_t pid, int how) /* {{{ */
126{
127 int s = 0;
128
129 switch (how) {
130 case FPM_PCTL_TERM :
131 s = SIGTERM;
132 break;
133 case FPM_PCTL_STOP :
134 s = SIGSTOP;
135 break;
136 case FPM_PCTL_CONT :
137 s = SIGCONT;
138 break;
139 case FPM_PCTL_QUIT :
140 s = SIGQUIT;
141 break;
142 case FPM_PCTL_KILL:
143 s = SIGKILL;
144 break;
145 default :
146 break;
147 }
148 return kill(pid, s);
149}
150/* }}} */
151
152void fpm_pctl_kill_all(int signo) /* {{{ */
153{
154 struct fpm_worker_pool_s *wp;
155 int alive_children = 0;
156
157 for (wp = fpm_worker_all_pools; wp; wp = wp->next) {
158 struct fpm_child_s *child;
159
160 for (child = wp->children; child; child = child->next) {
161 int res = kill(child->pid, signo);
162
163 zlog(ZLOG_DEBUG, "[pool %s] sending signal %d %s to child %d",
164 child->wp->config->name, signo,
165 fpm_signal_names[signo] ? fpm_signal_names[signo] : "", (int) child->pid);
166
167 if (res == 0) {
168 ++alive_children;
169 }
170 }
171 }
172
173 if (alive_children) {
174 zlog(ZLOG_DEBUG, "%d child(ren) still alive", alive_children);
175 }
176}
177/* }}} */
178
179static void fpm_pctl_action_next(void)
180{
181 int sig, timeout;
182
183 if (!fpm_globals.running_children) {
184 fpm_pctl_action_last();
185 }
186
187 if (fpm_signal_sent == 0) {
188 if (fpm_state == FPM_PCTL_STATE_TERMINATING) {
189 sig = SIGTERM;
190 } else {
191 sig = SIGQUIT;
192 }
193 timeout = fpm_global_config.process_control_timeout;
194 } else {
195 if (fpm_signal_sent == SIGQUIT) {
196 sig = SIGTERM;
197 } else {
198 sig = SIGKILL;
199 }
200 timeout = 1;
201 }
202
204 fpm_signal_sent = sig;
205 fpm_pctl_timeout_set(timeout);
206}
207
208void fpm_pctl(int new_state, int action) /* {{{ */
209{
210 switch (action) {
212 if (fpm_state == new_state) { /* already in progress - just ignore duplicate signal */
213 return;
214 }
215
216 switch (fpm_state) { /* check which states can be overridden */
218 /* 'normal' can be overridden by any other state */
219 break;
221 /* 'reloading' can be overridden by 'finishing' */
222 if (new_state == FPM_PCTL_STATE_FINISHING) break;
225 /* 'reloading' and 'finishing' can be overridden by 'terminating' */
226 if (new_state == FPM_PCTL_STATE_TERMINATING) break;
229 /* nothing can override 'terminating' state */
230 zlog(ZLOG_DEBUG, "not switching to '%s' state, because already in '%s' state",
231 fpm_state_names[new_state], fpm_state_names[fpm_state]);
232 return;
233 /* TODO Add EMPTY_SWITCH_DEFAULT_CASE? */
234 }
235
236 fpm_signal_sent = 0;
237 fpm_state = new_state;
238
239 zlog(ZLOG_DEBUG, "switching to '%s' state", fpm_state_names[fpm_state]);
241
243 fpm_pctl_action_next();
244 break;
246 fpm_pctl_action_last();
247 break;
248
249 }
250}
251/* }}} */
252
254{
255 return fpm_state == FPM_PCTL_STATE_NORMAL;
256}
257
259{
260 if (fpm_state == FPM_PCTL_STATE_NORMAL) {
261 return 0;
262 }
263
264 if (!fpm_globals.running_children) {
266 }
267 return 0;
268}
269
271{
272 int i;
273
274 saved_argc = fpm_globals.argc;
275 saved_argv = malloc(sizeof(char *) * (saved_argc + 1));
276
277 if (!saved_argv) {
278 return -1;
279 }
280
281 for (i = 0; i < saved_argc; i++) {
282 saved_argv[i] = strdup(fpm_globals.argv[i]);
283
284 if (!saved_argv[i]) {
285 return -1;
286 }
287 }
288
289 saved_argv[i] = 0;
290
291 if (0 > fpm_cleanup_add(FPM_CLEANUP_ALL, fpm_pctl_cleanup, 0)) {
292 return -1;
293 }
294 return 0;
295}
296
297static void fpm_pctl_check_request_timeout(struct timeval *now) /* {{{ */
298{
299 struct fpm_worker_pool_s *wp;
300
301 for (wp = fpm_worker_all_pools; wp; wp = wp->next) {
302 int track_finished = wp->config->request_terminate_timeout_track_finished;
303 int terminate_timeout = wp->config->request_terminate_timeout;
304 int slowlog_timeout = wp->config->request_slowlog_timeout;
305 struct fpm_child_s *child;
306
307 if (terminate_timeout || slowlog_timeout) {
308 for (child = wp->children; child; child = child->next) {
309 fpm_request_check_timed_out(child, now, terminate_timeout, slowlog_timeout, track_finished);
310 }
311 }
312 }
313}
314/* }}} */
315
316static void fpm_pctl_kill_idle_child(struct fpm_child_s *child) /* {{{ */
317{
318 if (child->idle_kill) {
320 } else {
321 child->idle_kill = true;
323 }
324}
325/* }}} */
326
327static void fpm_pctl_perform_idle_server_maintenance(struct timeval *now) /* {{{ */
328{
329 struct fpm_worker_pool_s *wp;
330
331 for (wp = fpm_worker_all_pools; wp; wp = wp->next) {
332 struct fpm_child_s *child;
333 struct fpm_child_s *last_idle_child = NULL;
334 int idle = 0;
335 int active = 0;
336 int children_to_fork;
337 unsigned cur_lq = 0;
338
339 if (wp->config == NULL) continue;
340
341 /* update status structure for all PMs */
344 cur_lq = 0;
345#if 0
346 } else {
347 if (cur_lq > 0) {
348 if (!wp->warn_lq) {
349 zlog(ZLOG_WARNING, "[pool %s] listening queue is not empty, #%d requests are waiting to be served, consider raising pm.max_children setting (%d)", wp->config->name, cur_lq, wp->config->pm_max_children);
350 wp->warn_lq = 1;
351 }
352 } else {
353 wp->warn_lq = 0;
354 }
355#endif
356 }
357 }
358
360
361 for (child = wp->children; child; child = child->next) {
362 if (fpm_request_is_idle(child)) {
363 if (last_idle_child == NULL) {
364 last_idle_child = child;
365 } else {
366 if (timercmp(&child->started, &last_idle_child->started, <)) {
367 last_idle_child = child;
368 }
369 }
370 idle++;
371 } else {
372 active++;
373 }
374 }
375
377
378 /* this is specific to PM_STYLE_ONDEMAND */
379 if (wp->config->pm == PM_STYLE_ONDEMAND) {
380 struct timeval last, now;
381
382 zlog(ZLOG_DEBUG, "[pool %s] currently %d active children, %d spare children", wp->config->name, active, idle);
383
384 if (!last_idle_child) continue;
385
386 fpm_request_last_activity(last_idle_child, &last);
388 if (last.tv_sec < now.tv_sec - wp->config->pm_process_idle_timeout) {
389 fpm_pctl_kill_idle_child(last_idle_child);
390 }
391
392 continue;
393 }
394
395 /* the rest is only used by PM_STYLE_DYNAMIC */
396 if (wp->config->pm != PM_STYLE_DYNAMIC) continue;
397
398 zlog(ZLOG_DEBUG, "[pool %s] currently %d active children, %d spare children, %d running children. Spawning rate %d", wp->config->name, active, idle, wp->running_children, wp->idle_spawn_rate);
399
400 if (idle > wp->config->pm_max_spare_servers && last_idle_child) {
401 fpm_pctl_kill_idle_child(last_idle_child);
402 wp->idle_spawn_rate = 1;
403 continue;
404 }
405
406 if (idle < wp->config->pm_min_spare_servers) {
407 if (wp->running_children >= wp->config->pm_max_children) {
408 if (!wp->warn_max_children && !wp->shared) {
409 fpm_scoreboard_update(0, 0, 0, 0, 0, 1, 0, 0, FPM_SCOREBOARD_ACTION_INC, wp->scoreboard);
410 zlog(ZLOG_WARNING, "[pool %s] server reached pm.max_children setting (%d), consider raising it", wp->config->name, wp->config->pm_max_children);
411 wp->warn_max_children = 1;
412 }
413 wp->idle_spawn_rate = 1;
414 continue;
415 }
416
417 if (wp->idle_spawn_rate >= 8) {
418 zlog(ZLOG_WARNING, "[pool %s] seems busy (you may need to increase pm.start_servers, or pm.min/max_spare_servers), spawning %d children, there are %d idle, and %d total children", wp->config->name, wp->idle_spawn_rate, idle, wp->running_children);
419 }
420
421 /* compute the number of idle process to spawn */
422 children_to_fork = MIN(wp->idle_spawn_rate, wp->config->pm_min_spare_servers - idle);
423
424 /* get sure it won't exceed max_children */
425 children_to_fork = MIN(children_to_fork, wp->config->pm_max_children - wp->running_children);
426 if (children_to_fork <= 0) {
427 if (!wp->warn_max_children && !wp->shared) {
428 fpm_scoreboard_update(0, 0, 0, 0, 0, 1, 0, 0, FPM_SCOREBOARD_ACTION_INC, wp->scoreboard);
429 zlog(ZLOG_WARNING, "[pool %s] server reached pm.max_children setting (%d), consider raising it", wp->config->name, wp->config->pm_max_children);
430 wp->warn_max_children = 1;
431 }
432 wp->idle_spawn_rate = 1;
433 continue;
434 }
435 wp->warn_max_children = 0;
436
437 fpm_children_make(wp, 1, children_to_fork, 1);
438
439 /* if it's a child, stop here without creating the next event
440 * this event is reserved to the master process
441 */
442 if (fpm_globals.is_child) {
443 return;
444 }
445
446 zlog(ZLOG_DEBUG, "[pool %s] %d child(ren) have been created dynamically", wp->config->name, children_to_fork);
447
448 /* Double the spawn rate for the next iteration */
450 wp->idle_spawn_rate *= 2;
451 }
452 continue;
453 }
454 wp->idle_spawn_rate = 1;
455 }
456}
457/* }}} */
458
459void fpm_pctl_heartbeat(struct fpm_event_s *ev, short which, void *arg) /* {{{ */
460{
461 static struct fpm_event_s heartbeat;
462 struct timeval now;
463
464 if (fpm_globals.parent_pid != getpid()) {
465 return; /* sanity check */
466 }
467
468 if (which == FPM_EV_TIMEOUT) {
470 fpm_pctl_check_request_timeout(&now);
471 return;
472 }
473
474 /* ensure heartbeat is not lower than FPM_PCTL_MIN_HEARTBEAT */
475 fpm_globals.heartbeat = MAX(fpm_globals.heartbeat, FPM_PCTL_MIN_HEARTBEAT);
476
477 /* first call without setting to initialize the timer */
478 zlog(ZLOG_DEBUG, "heartbeat have been set up with a timeout of %dms", fpm_globals.heartbeat);
480 fpm_event_add(&heartbeat, fpm_globals.heartbeat);
481}
482/* }}} */
483
484void fpm_pctl_perform_idle_server_maintenance_heartbeat(struct fpm_event_s *ev, short which, void *arg) /* {{{ */
485{
486 static struct fpm_event_s heartbeat;
487 struct timeval now;
488
489 if (fpm_globals.parent_pid != getpid()) {
490 return; /* sanity check */
491 }
492
493 if (which == FPM_EV_TIMEOUT) {
496 fpm_pctl_perform_idle_server_maintenance(&now);
497
498 /* if it's a child, stop here without creating the next event
499 * this event is reserved to the master process
500 */
501 if (fpm_globals.is_child) {
502 return;
503 }
504 }
505 return;
506 }
507
508 /* first call without setting which to initialize the timer */
511}
512/* }}} */
513
514void fpm_pctl_on_socket_accept(struct fpm_event_s *ev, short which, void *arg) /* {{{ */
515{
516 struct fpm_worker_pool_s *wp = (struct fpm_worker_pool_s *)arg;
517 struct fpm_child_s *child;
518
519
520 if (fpm_globals.parent_pid != getpid()) {
521 /* prevent a event race condition when child process
522 * have not set up its own event loop */
523 return;
524 }
525
526 wp->socket_event_set = 0;
527
528/* zlog(ZLOG_DEBUG, "[pool %s] heartbeat running_children=%d", wp->config->name, wp->running_children);*/
529
531 if (!wp->warn_max_children && !wp->shared) {
533 zlog(ZLOG_WARNING, "[pool %s] server reached max_children setting (%d), consider raising it", wp->config->name, wp->config->pm_max_children);
535 }
536
537 return;
538 }
539
540 for (child = wp->children; child; child = child->next) {
541 /* if there is at least on idle child, it will handle the connection, stop here */
542 if (fpm_request_is_idle(child)) {
543 return;
544 }
545 }
547 fpm_children_make(wp, 1, 1, 1);
548
549 if (fpm_globals.is_child) {
550 return;
551 }
552
553 zlog(ZLOG_DEBUG, "[pool %s] got accept without idle child available .... I forked", wp->config->name);
554}
555/* }}} */
char s[4]
Definition cdf.c:77
zend_string * res
Definition ffi.c:4692
zval * arg
Definition ffi.c:3975
struct fpm_globals_s fpm_globals
Definition fpm.c:24
#define FPM_EXIT_SOFTWARE
Definition fpm.h:27
#define FPM_EXIT_OK
Definition fpm.h:15
int fpm_children_make(struct fpm_worker_pool_s *wp, int in_event_loop, int nb_to_spawn, int is_debug)
int fpm_cleanup_add(int type, void(*cleanup)(int, void *), void *arg)
Definition fpm_cleanup.c:18
void fpm_cleanups_run(int type)
Definition fpm_cleanup.c:36
@ FPM_CLEANUP_PARENT_EXEC
Definition fpm_cleanup.h:13
@ FPM_CLEANUP_PARENT_EXIT_MAIN
Definition fpm_cleanup.h:12
@ FPM_CLEANUP_ALL
Definition fpm_cleanup.h:15
int fpm_clock_get(struct timeval *tv)
Definition fpm_clock.c:110
int fpm_conf_unlink_pid(void)
Definition fpm_conf.c:1240
struct fpm_global_config_s fpm_global_config
Definition fpm_conf.c:64
@ PM_STYLE_ONDEMAND
Definition fpm_conf.h:121
@ PM_STYLE_DYNAMIC
Definition fpm_conf.h:120
#define timercmp(a, b, CMP)
Definition fpm_config.h:62
int fpm_event_add(struct fpm_event_s *ev, unsigned long int frequency)
Definition fpm_events.c:496
#define fpm_event_set_timer(ev, flags, cb, arg)
Definition fpm_events.h:11
#define FPM_EV_TIMEOUT
Definition fpm_events.h:6
#define FPM_EV_PERSIST
Definition fpm_events.h:8
void fpm_pctl(int new_state, int action)
int fpm_pctl_init_main(void)
int fpm_pctl_can_spawn_children(void)
int fpm_pctl_kill(pid_t pid, int how)
#define optional_arg(c)
void fpm_pctl_on_socket_accept(struct fpm_event_s *ev, short which, void *arg)
void fpm_pctl_heartbeat(struct fpm_event_s *ev, short which, void *arg)
void fpm_pctl_kill_all(int signo)
int fpm_pctl_child_exited(void)
void fpm_pctl_perform_idle_server_maintenance_heartbeat(struct fpm_event_s *ev, short which, void *arg)
@ FPM_PCTL_STATE_TERMINATING
@ FPM_PCTL_STATE_NORMAL
@ FPM_PCTL_STATE_FINISHING
@ FPM_PCTL_STATE_RELOADING
@ FPM_PCTL_STATE_UNSPECIFIED
@ FPM_PCTL_ACTION_SET
@ FPM_PCTL_ACTION_TIMEOUT
@ FPM_PCTL_ACTION_LAST_CHILD_EXITED
@ FPM_PCTL_CONT
@ FPM_PCTL_KILL
@ FPM_PCTL_TERM
@ FPM_PCTL_QUIT
@ FPM_PCTL_STOP
#define FPM_IDLE_SERVER_MAINTENANCE_HEARTBEAT
#define FPM_PCTL_MIN_HEARTBEAT
void fpm_request_check_timed_out(struct fpm_child_s *child, struct timeval *now, int terminate_timeout, int slowlog_timeout, int track_finished)
int fpm_request_last_activity(struct fpm_child_s *child, struct timeval *tv)
int fpm_request_is_idle(struct fpm_child_s *child)
void fpm_scoreboard_update_begin(struct fpm_scoreboard_s *scoreboard)
void fpm_scoreboard_update_commit(int idle, int active, int lq, int lq_len, int requests, int max_children_reached, int slow_rq, size_t memory_peak, int action, struct fpm_scoreboard_s *scoreboard)
void fpm_scoreboard_update(int idle, int active, int lq, int lq_len, int requests, int max_children_reached, int slow_rq, size_t memory_peak, int action, struct fpm_scoreboard_s *scoreboard)
#define FPM_SCOREBOARD_ACTION_INC
#define FPM_SCOREBOARD_ACTION_SET
const char * fpm_signal_names[NSIG+1]
Definition fpm_signals.c:25
int fpm_signals_block(void)
int fpm_socket_get_listening_queue(int sock, unsigned *cur_lq, unsigned *max_lq)
int fpm_stdio_restore_original_stderr(int close_after_restore)
Definition fpm_stdio.c:91
struct fpm_worker_pool_s * fpm_worker_all_pools
@ FPM_AF_INET
#define NULL
Definition gdcache.h:45
const SIGKILL
const SIGTERM
const SIGQUIT
const SIGCONT
const SIGSTOP
php_output_handler * active
Definition php_output.h:140
time_t now
Definition session.c:1281
struct timeval started
struct fpm_child_s * next
struct fpm_worker_pool_s * wp
short which
Definition fpm_events.h:21
int request_terminate_timeout_track_finished
Definition fpm_conf.h:87
struct fpm_worker_pool_config_s * config
enum fpm_address_domain listen_address_domain
struct fpm_worker_pool_s * shared
struct fpm_scoreboard_s * scoreboard
struct fpm_worker_pool_s * next
struct fpm_child_s * children
exit(string|int $status=0)
int last
#define MIN(a, b)
#define ZEND_FALLTHROUGH
#define MAX(a, b)
#define ZLOG_SYSERROR
Definition zlog.h:53
@ ZLOG_DEBUG
Definition zlog.h:42
@ ZLOG_NOTICE
Definition zlog.h:43
@ ZLOG_WARNING
Definition zlog.h:44
#define zlog(flags,...)
Definition zlog.h:9