int fpm_children_make(struct fpm_worker_pool_s *wp, int in_event_loop, int nb_to_spawn, int is_debug) /* {{{ */ { pid_t pid; struct fpm_child_s *child; int max; if (wp->config->pm == PM_STYLE_DYNAMIC) { if (!in_event_loop) { /* starting */ max = wp->config->pm_start_servers; } else { max = wp->running_children + nb_to_spawn; } } else { /* PM_STYLE_STATIC */ max = wp->config->pm_max_children; } while (fpm_pctl_can_spawn_children() && wp->running_children < max) { child = fpm_resources_prepare(wp); if (!child) { return 2; } pid = fork(); switch (pid) { case 0 : fpm_child_resources_use(child); fpm_globals.is_child = 1; fpm_child_init(wp); return 0; case -1 : zlog(ZLOG_SYSERROR, "fork() failed"); fpm_resources_discard(child); return 2; default : child->pid = pid; fpm_clock_get(&child->started); fpm_parent_resources_use(child); zlog(is_debug ? ZLOG_DEBUG : ZLOG_NOTICE, "[pool %s] child %d started", wp->config->name, (int) pid); } } return 1; /* we are done */ }
int fpm_children_make(struct fpm_worker_pool_s *wp, int in_event_loop, int nb_to_spawn, int is_debug) /* {{{ */ { pid_t pid; struct fpm_child_s *child; int max; static int warned = 0; //动态模式 if (wp->config->pm == PM_STYLE_DYNAMIC) { if (!in_event_loop) { /* starting */ max = wp->config->pm_start_servers; } else { max = wp->running_children + nb_to_spawn; } } else if (wp->config->pm == PM_STYLE_ONDEMAND) { //ondemand模式 if (!in_event_loop) { /* starting */ max = 0; /* do not create any child at startup */ } else { max = wp->running_children + nb_to_spawn; } } else { /* PM_STYLE_STATIC */ //静态模式 max = wp->config->pm_max_children; } /* * fork children while: * - fpm_pctl_can_spawn_children : FPM is running in a NORMAL state (aka not restart, stop or reload) * - wp->running_children < max : there is less than the max process for the current pool * - (fpm_global_config.process_max < 1 || fpm_globals.running_children < fpm_global_config.process_max): * if fpm_global_config.process_max is set, FPM has not fork this number of processes (globaly) */ //当执行restart、stop、reload命令时,fpm_pctl_can_spawn_children()为false while (fpm_pctl_can_spawn_children() && wp->running_children < max && (fpm_global_config.process_max < 1 || fpm_globals.running_children < fpm_global_config.process_max)) { warned = 0; child = fpm_resources_prepare(wp); if (!child) { return 2; } //fork子进程 pid = fork(); switch (pid) { case 0 : fpm_child_resources_use(child); fpm_globals.is_child = 1; //子进程初始化 fpm_child_init(wp); return 0; case -1 : zlog(ZLOG_SYSERROR, "fork() failed"); fpm_resources_discard(child); return 2; default : child->pid = pid; fpm_clock_get(&child->started); fpm_parent_resources_use(child); zlog(is_debug ? ZLOG_DEBUG : ZLOG_NOTICE, "[pool %s] child %d started", wp->config->name, (int) pid); } } if (!warned && fpm_global_config.process_max > 0 && fpm_globals.running_children >= fpm_global_config.process_max) { warned = 1; zlog(ZLOG_WARNING, "The maximum number of processes has been reached. Please review your configuration and consider raising 'process.max'"); } return 1; /* we are done */ }
void fpm_children_bury() /* {{{ */ { int status; pid_t pid; struct fpm_child_s *child; while ( (pid = waitpid(-1, &status, WNOHANG | WUNTRACED)) > 0) { char buf[128]; int severity = ZLOG_NOTICE; int restart_child = 1; child = fpm_child_find(pid); /* WIFEXITED(status)如果子进程正常结束则为非0值。 WEXITSTATUS(status)取得子进程exit()返回的结束代码,一般会先用WIFEXITED 来判断是否正常结束才能使用此宏。 WIFSIGNALED(status)如果子进程是因为信号而结束则此宏值为真 WTERMSIG(status)取得子进程因信号而中止的信号代码,一般会先用WIFSIGNALED 来判断后才使用此宏。 WIFSTOPPED(status)如果子进程处于暂停执行情况则此宏值为真。一般只有使用WUNTRACED 时才会有此情况。 WSTOPSIG(status)取得引发子进程暂停的信号代码, */ if (WIFEXITED(status)) { snprintf(buf, sizeof(buf), "with code %d", WEXITSTATUS(status)); /* if it's been killed because of dynamic process management * don't restart it automaticaly */ if (child && child->idle_kill) { restart_child = 0; } if (WEXITSTATUS(status) != FPM_EXIT_OK) { severity = ZLOG_WARNING; } } else if (WIFSIGNALED(status)) { const char *signame = fpm_signal_names[WTERMSIG(status)]; const char *have_core = WCOREDUMP(status) ? " - core dumped" : ""; if (signame == NULL) { signame = ""; } snprintf(buf, sizeof(buf), "on signal %d (%s%s)", WTERMSIG(status), signame, have_core); /* if it's been killed because of dynamic process management * don't restart it automaticaly */ if (child && child->idle_kill && WTERMSIG(status) == SIGQUIT) { restart_child = 0; } if (WTERMSIG(status) != SIGQUIT) { /* possible request loss */ severity = ZLOG_WARNING; } } else if (WIFSTOPPED(status)) { zlog(ZLOG_NOTICE, "child %d stopped for tracing", (int) pid); //回调 php_trace.c if (child && child->tracer) { child->tracer(child); } continue; } if (child) { struct fpm_worker_pool_s *wp = child->wp; struct timeval tv1, tv2; fpm_child_unlink(child); //释放scoreboard fpm_scoreboard_proc_free(wp->scoreboard, child->scoreboard_i); fpm_clock_get(&tv1); timersub(&tv1, &child->started, &tv2); if (restart_child) { if (!fpm_pctl_can_spawn_children()) { severity = ZLOG_DEBUG; } zlog(severity, "[pool %s] child %d exited %s after %ld.%06d seconds from start", child->wp->config->name, (int) pid, buf, tv2.tv_sec, (int) tv2.tv_usec); } else { zlog(ZLOG_DEBUG, "[pool %s] child %d has been killed by the process managment after %ld.%06d seconds from start", child->wp->config->name, (int) pid, tv2.tv_sec, (int) tv2.tv_usec); } fpm_child_close(child, 1 /* in event_loop */); fpm_pctl_child_exited(); if (last_faults && (WTERMSIG(status) == SIGSEGV || WTERMSIG(status) == SIGBUS)) { time_t now = tv1.tv_sec; int restart_condition = 1; int i; last_faults[fault++] = now; if (fault == fpm_global_config.emergency_restart_threshold) { fault = 0; } for (i = 0; i < fpm_global_config.emergency_restart_threshold; i++) { if (now - last_faults[i] > fpm_global_config.emergency_restart_interval) { restart_condition = 0; break; } } if (restart_condition) { zlog(ZLOG_WARNING, "failed processes threshold (%d in %d sec) is reached, initiating reload", fpm_global_config.emergency_restart_threshold, fpm_global_config.emergency_restart_interval); fpm_pctl(FPM_PCTL_STATE_RELOADING, FPM_PCTL_ACTION_SET); } } if (restart_child) { fpm_children_make(wp, 1 /* in event loop */, 1, 0); if (fpm_globals.is_child) { break; } } } else { zlog(ZLOG_ALERT, "oops, unknown child (%d) exited %s. Please open a bug report (https://bugs.php.net).", pid, buf); } } }