Analysis of source code of operation mechanism of PHP5.6.15-FPM

Source: Internet
Author: User
Tags signal handler

SOURCE version: Php5.6.15

SOURCE directory: SAPI/FPM/FPM

Description: The main function of the source code is directly annotated above

=============>>start<<================================

Main process signal initialization, processing according to the type of signal received

int Fpm_signals_init_main ()/* {{{* *
{
struct Sigaction Act; Create sigaction structure and bind signal handle function

Create Sockpair, multi-channel IO multiplexing
if (0 > Socketpair (Af_unix, sock_stream, 0, sp)) {
Zlog (Zlog_syserror, "Failed to init Signals:socketpair ()");
return-1;
}
Set non-blocking
if (0 > fd_set_blocked (sp[0], 0) | | 0 > fd_set_blocked (sp[1], 0)) {
Zlog (Zlog_syserror, "Failed to init signals:fd_set_blocked ()");
return-1;
}
Set FD to hold state, do not close
if (0 > Fcntl (sp[0], F_SETFD, fd_cloexec) | | 0 > fcntl (sp[1], F_SETFD, fd_cloexec)) {
Zlog (Zlog_syserror, "Falied to Init signals:fcntl (F_SETFD, Fd_cloexec)");
return-1;
}

memset (&act, 0, sizeof (ACT));
Act.sa_handler = Sig_handler; Set up signal processing functions
Sigfillset (&act.sa_mask); Initializing the signal set

Adding a set of processed signals
if (0 > Sigaction (SIGTERM, &act, 0) | |
0 > Sigaction (SIGINT, &act, 0) | |
0 > Sigaction (SIGUSR1, &act, 0) | |
0 > Sigaction (SIGUSR2, &act, 0) | |
0 > Sigaction (SIGCHLD, &act, 0) | |
0 > Sigaction (sigquit, &act, 0)) {

Zlog (Zlog_syserror, "Failed to init signals:sigaction ()");
return-1;
}
return 0;
}
/* }}} */

Signal processing function, according to the corresponding signal, the corresponding character is written to Sp[1]
static void Sig_handler (int signo)/* {{{* * *
{
static const char Sig_chars[nsig + 1] = {
[SIGTERM] = ' T ',
[SIGINT] = ' I ',
[SIGUSR1] = ' 1 ',
[SIGUSR2] = ' 2 ',
[Sigquit] = ' Q ',
[SIGCHLD] = ' C '
};
char s;
int Saved_errno;

if (fpm_globals.parent_pid! = Getpid ()) {
/* Prevent a signal race condition when child process
There is not set up it s own signal handler yet */
Return
}

Saved_errno = errno;
s = Sig_chars[signo];
Write (sp[1], &s, sizeof (s));
errno = Saved_errno;
}
/* }}} */


Child process signal initialization, processed according to the type of signal received

int Fpm_signals_init_child ()/* {{{* *
{
struct Sigaction Act, ACT_DFL;

memset (&act, 0, sizeof (ACT));
memset (&ACT_DFL, 0, sizeof (ACT_DFL));

Act.sa_handler = &sig_soft_quit; Call signal processing function, processing only sigquit signal
Act.sa_flags |= Sa_restart;

Act_dfl.sa_handler = SIG_DFL; Default Signal Processing method

Close the socket pair
Close (sp[0]);
Close (sp[1]);

if (0 > Sigaction (SIGTERM, &ACT_DFL, 0) | |
0 > Sigaction (SIGINT, &ACT_DFL, 0) | |
0 > Sigaction (SIGUSR1, &ACT_DFL, 0) | |
0 > Sigaction (SIGUSR2, &ACT_DFL, 0) | |
0 > Sigaction (SIGCHLD, &ACT_DFL, 0) | |
0 > Sigaction (sigquit, &act, 0)) {

Zlog (Zlog_syserror, "Failed to init child signals:sigaction ()");
return-1;
}
return 0;
}
/* }}} */

Sub-process sigquit signal processing function
static void Sig_soft_quit (int signo)/* {{{* * *
{
int saved_errno = errno;

/* Closing FASTCGI listening socket would force fcgi_accept () exit immediately */
Close (0);
if (0 > socket (af_unix, sock_stream, 0)) {
Zlog (Zlog_warning, "failed to create a new socket");
}
Fpm_php_soft_quit ();
errno = Saved_errno;
}
/* }}} */

void Fpm_event_loop (int err)/* {{{* * *
{
static struct fpm_event_s signal_fd_event;

/* Sanity Check */
if (fpm_globals.parent_pid! = Getpid ()) {
Return
}

Create an FD collection, put fd[0] into the FD set to listen for FD changes, and set the corresponding processing function fpm_got_signal
Fpm_event_set (&signal_fd_event, FPM_SIGNALS_GET_FD (), Fpm_ev_read, &fpm_got_signal, NULL);
Fpm_event_add (&signal_fd_event, 0);

/* Add timers */
if (Fpm_globals.heartbeat > 0) {
Fpm_pctl_heartbeat (NULL, 0, NULL);
}

if (!err) {
Fpm_pctl_perform_idle_server_maintenance_heartbeat (NULL, 0, NULL);

Zlog (zlog_debug, "%zu bytes has been reserved in SHM", fpm_shm_get_size_allocated ());
Zlog (Zlog_notice, "ready to handle connections");

#ifdef HAVE_SYSTEMD
Fpm_systemd_heartbeat (NULL, 0, NULL);
#endif
}

while (1) {
struct fpm_event_queue_s *q, *q2;
struct Timeval ms;
struct Timeval tmp;
struct Timeval now;
unsigned long int timeout;
int ret;

/* Sanity Check */
if (fpm_globals.parent_pid! = Getpid ()) {
Return
}

Fpm_clock_get (&now);
Timerclear (&MS);

/* Search in the timeout queue for the next timer to trigger */
Q = Fpm_event_queue_timer;
while (q) {
if (!timerisset (&ms)) {
ms = q->ev->timeout;
} else {
if (timercmp (&q->ev->timeout, &ms, <)) {
ms = q->ev->timeout;
}
}
Q = q->next;
}

/* 1s timeout if none has been set */
if (!timerisset (&ms) | | timercmp (&MS, &now, <) | | timercmp (&MS, &now, = =)) {
timeout = 1000;
} else {
Timersub (&ms, &now, &tmp);
Timeout = (TMP.TV_SEC * +) + (tmp.tv_usec/1000) + 1;
}

ret = module->wait (FPM_EVENT_QUEUE_FD, timeout);

/* is a child, nothing to does here */
if (ret = =-2) {
Return
}

if (Ret > 0) {
Zlog (Zlog_debug, "event module triggered%d events", ret);
}

/* Trigger Timers */
Q = Fpm_event_queue_timer;
while (q) {
Fpm_clock_get (&now);
if (Q->ev) {
if (timercmp (&now, &q->ev->timeout, >) | | timercmp (&now, &q->ev->timeout, = =)) {
Fpm_event_fire (Q->ev);
/* Sanity Check */
if (fpm_globals.parent_pid! = Getpid ()) {
Return
}
if (Q->ev->flags & fpm_ev_persist) {
Fpm_event_set_timeout (Q->ev, now);
} else {/* Delete the event */
q2 = q;
if (Q->prev) {
Q->prev->next = q->next;
}
if (Q->next) {
Q->next->prev = q->prev;
}
if (q = = Fpm_event_queue_timer) {
Fpm_event_queue_timer = q->next;
if (Fpm_event_queue_timer) {
Fpm_event_queue_timer->prev = NULL;
}
}
Q = q->next;
Free (q2);
Continue
}
}
}
Q = q->next;
}
}
}
/* }}} */

static void Fpm_got_signal (struct fpm_event_s *ev, short which, void *arg)/* {{* * *
{
char c;
int res, RET;
int FD = ev->fd;

do {
do {
res = read (FD, &c, 1); Take the value from fd[0] and put the variable C
} while (res = =-1 && errno = = eintr);

if (res <= 0) {
if (Res < 0 && errno! = Eagain && errno! = Ewouldblock) {
Zlog (Zlog_syserror, "unable to read from the signal pipe");
}
Return
}
Depending on the signal C, different logic is executed
Switch (c) {
Sub-signal exits or pauses
Case ' C ':/* SIGCHLD */
Zlog (Zlog_debug, "received SIGCHLD");
Fpm_children_bury ();
Break
Receive signal to terminate process
Case ' I ':/* SIGINT */
Zlog (Zlog_debug, "received SIGINT");
Zlog (Zlog_notice, "terminating ...");
Fpm_pctl (fpm_pctl_state_terminating, Fpm_pctl_action_set);
Break
Ditto
Case ' T ':/* SIGTERM */
Zlog (Zlog_debug, "received SIGTERM");
Zlog (Zlog_notice, "terminating ...");
Fpm_pctl (fpm_pctl_state_terminating, Fpm_pctl_action_set);
Break
Process Exit signal
Case ' Q ':/* sigquit */
Zlog (Zlog_debug, "received Sigquit");
Zlog (Zlog_notice, "finishing ...");
Fpm_pctl (fpm_pctl_state_finishing, Fpm_pctl_action_set);
Break
Re-open log signal
Case ' 1 ':/* SIGUSR1 */
Zlog (Zlog_debug, "received SIGUSR1");
if (0 = = Fpm_stdio_open_error_log (1)) {
Zlog (zlog_notice, "error log file re-opened");
} else {
Zlog (Zlog_error, "Unable to re-opened ERROR log file");
}

ret = Fpm_log_open (1);
if (ret = = 0) {
Zlog (zlog_notice, "Access log file re-opened");
} else if (ret = =-1) {
Zlog (Zlog_error, "Unable to re-opened access log file");
}
/* Else no access log is set */

Break
Reload Signal
Case ' 2 ':/* SIGUSR2 */
Zlog (Zlog_debug, "received SIGUSR2");
Zlog (Zlog_notice, "reloading in progress ...");
Fpm_pctl (fpm_pctl_state_reloading, Fpm_pctl_action_set);
Break
}

if (fpm_globals.is_child) {
Break
}
} while (1);
Return
}
/* }}} */

Below the processing of various signals to do a bit of analysis

Sub-signal exit or pause is send signal sigchld, the most understand will call Fpm_children_bury function, in FPM_CHILDREN.C

void Fpm_children_bury ()/* {{{* * *
{
int status;
pid_t pid;
struct fpm_child_s *child;
The child process ends, returns the PID execution loop, or exits
while (PID = Waitpid ( -1, &status, Wnohang | wuntraced)) > 0) {
Char buf[128];
int severity = Zlog_notice;
int restart_child = 1;

Child = Fpm_child_find (PID); Gets the retired child process

Normal exit
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;
}
Receive Exit signal
} 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;
}
Pause Exit Signal
} else if (wifstopped (status)) {

Zlog (Zlog_notice, "child%d stopped for tracing", (int) PID);

if (child && child->tracer) {
Master process recovers child process through tracer
Child->tracer (child);
}

Continue
}

if (child) {
struct fpm_worker_pool_s *wp = child->wp;
struct Timeval TV1, TV2;

Fpm_child_unlink (child);

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.%0 6d 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 have been killed by the process management after%ld.%0 6d 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 (); Main process exit
The number of abnormally exited threads is greater than the configuration item, reload
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) {
If the child process exits abnormally, fork a
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);
}
}
}
/* }}} */

PHP uses most of the reload operation, corresponding to the SIGUSR2 signal, corresponding to the processing function fpm_pctl

void Fpm_pctl (int new_state, int action)/* * {{{*/
{
Switch (action) {
Case fpm_pctl_action_set:
if (fpm_s Tate = = new_state) {/* already in progress-just ignore duplicate signal */
return;
}
//fpm_state
Switch (fpm_state) {/* Check which states can be overridden */
Case Fpm_pctl_state_normal:
/* ' Normal ' can is overridden by any and state */
break;
Case fpm_pctl_state_reloading:
/* ' reloading ' can is overridden by ' finishing ' */
if (new_state = = Fpm_pctl_state_f inishing) break;
Case fpm_pctl_state_finishing:
/* ' reloading ' and ' finishing ' can is overridden by ' terminating ' */
if (new_sta Te = = fpm_pctl_state_terminating) break;
Case fpm_pctl_state_terminating:
/No can override ' terminating ' state */
Zlog (Zlog_debug, "not Switching to '%s ', because already in '%s ' state ',
Fpm_state_names[new_state], fpm_state_names[fpm_state]);
return;
}

fpm_signal_sent = 0;
Fpm_state = new_state;

Zlog (Zlog_debug, "Switching to '%s ' state", fpm_state_names[fpm_state]);
/* Fall Down */
Because there is no break statement, Fpm_pctl_action_next will execute
Case Fpm_pctl_action_timeout:
Fpm_pctl_action_next ();
Break
Case fpm_pctl_action_last_child_exited:
Fpm_pctl_action_last ();
Break

}
}
/* }}} */


/** function Description
* * Send a signal to all processes
Register a timer, re-send the signal at the end of the child process, and write C to sp[1] if the normal end, execute Fpm_children_bury
* Final call to fpm_pctl_child_exited until the end of the last child process.
**/
static void Fpm_pctl_action_next ()/* {{{* * *
{
int sig, timeout;

if (!fpm_globals.running_children) {
Fpm_pctl_action_last ();
}

if (fpm_signal_sent = = 0) {
if (fpm_state = = fpm_pctl_state_terminating) {
sig = SIGTERM;
} else {
sig = Sigquit;
}
Timeout = fpm_global_config.process_control_timeout;
} else {
if (fpm_signal_sent = = Sigquit) {
sig = SIGTERM;
} else {
sig = SIGKILL;
}
Timeout = 1;
}

Fpm_pctl_kill_all (SIG); Send Signal
Fpm_signal_sent = sig;
Fpm_pctl_timeout_set (timeout); Set the timeout function and eventually call Fpm_pctl (Fpm_pctl_state_unspecified, fpm_pctl_action_timeout) to continue signaling
}
/* }}} */


static void Fpm_pctl_action (struct fpm_event_s *ev, short which, void *arg)/* {{* * *
{
Fpm_pctl (fpm_pctl_state_unspecified, fpm_pctl_action_timeout);
}
/* }}} */


If the child process exits normally, it will want to sp[1] write the sigchild signal, perform a new round of loops, execute the fpm_children_bury-->fpm_pctl_child_exited
When all child processes are processed, the fpm_pctl_action_last is called and the reload operation is performed

static void Fpm_pctl_action_last ()/* {{{* * *
{
Switch (fpm_state) {
Case fpm_pctl_state_reloading://Perform reload operation
Fpm_pctl_exec ();
Break

Case fpm_pctl_state_finishing:
Case fpm_pctl_state_terminating:
Fpm_pctl_exit ();
Break
}
}

static void Fpm_pctl_exec ()/* {{{* * *
{

Zlog (Zlog_notice, "RELOADING:EXECVP (\"%s\ ", {\"%s\ ""
"%s%s%s" "%s%s%s" "%s%s%s" "%s%s%s" "%s%s%s"
"%s%s%s" "%s%s%s" "%s%s%s" "%s%s%s" "%s%s%s"
"})",
Saved_argv[0], saved_argv[0],
Optional_arg (1),
Optional_arg (2),
Optional_arg (3),
Optional_arg (4),
Optional_arg (5),
Optional_arg (6),
Optduiional_arg (7),
Optional_arg (8),
Optional_arg (9),
Optional_arg (10)
);

Fpm_cleanups_run (fpm_cleanup_parent_exec);
EXECVP (Saved_argv[0], saved_argv); Process substitution, EXECVP will empty the old Process code area, data, stack, reload
Zlog (Zlog_syserror, "failed to RELOAD:EXECVP () failed");
Exit (Fpm_exit_software);
}
/* }}} */

The above source tracing, finally summarizes the reload logic process, its signal processing logic is no longer recalled.

1.FPM main process received SIGUSR2 signal, callback function to sp[1] Write 2 this character;
2.FPM Fpm_event_loop Hear data write (IO multiplexing) call callback function fpm_got_signal read signal identifier 2;
3. Execute fpm_pctl--> fpm_pctl_action_next, signal each sub-process and register a timer;
4. If the child process exits timeout, the main process will re-signal, normal exit the main process received sigchild signal, write identifier c corresponding to Sigchild;
5. Repeat 2, remove the C signal identifier to execute fpm_children_bury-->fpm_pctl_child_exited;
6. After all child processes are finished, enter Fpm_pctl_action_last interrupt reload operation, mainly call EXECVP to create a new process (Execvp execution method is code space, data space, stack substitution);

Analysis of source code of operation mechanism of PHP5.6.15-FPM

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.