Zusammenfassung: Durch das erneute Laden wird der Ruhezustand vorzeitig beendet. Daher haben wir untersucht, wie der Neuladevorgang von fpm implementiert werden kann.
Der Neuladeprozess von PHP-FPM
Dieser Artikel wird unter PHP7.0 fpm analysiert und die Einstellung von „process_control_timeout“ ist nicht 0.
Neustartsignal
Zunächst können wir daraus erkennen, dass der Neuladevorgang von fpm tatsächlich das USR2-Signal an den fpm-Prozess sendet.
Im Masterprozess von fpm wird die Signalverarbeitungsfunktion registriert durch:
int fpm_signals_init_main() /* {{{ */{ struct sigactionact; // 。。。。。。 memset(&act, 0, sizeof(act)); act.sa_handler = sig_handler; sigfillset(&act.sa_mask); 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; }/* }}} */
Kurz gesagt, alle Signale werden durch blockiert, und dann wird die entsprechende Signalverarbeitungsfunktion durch sigaction eingestellt .
Wenn wir fpm neu laden, sendet systemctl das USR2-Signal an den Masterprozess von fpm und führt die Funktion aus:
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; // *** s = sig_chars[signo]; zend_quiet_write(sp[1], &s, sizeof(s)); errno = saved_errno; }/* }}} */
Der entscheidende Punkt ist zend_quiet_write, also . Die Funktion sig_handler schreibt einen String 2 in sp[1].
Hier ist zu beachten, dass sp[0] und sp[1] lokale Sockets sind, die von erstellt wurden.
Der Master beginnt mit dem Neustart, wenn das Signal auftritt, aber die Hauptlogik des Programms wird trotzdem nicht gestört. Woher weiß der FPM-Master-Prozess, dass er neu geladen werden muss?
Die Antwort liegt in der Ereignisschleife des Masterprozesses.
Vor der Schleife müssen wir sp[0] verwenden, um eine Struktur fpm_event_s zum hörenden fd hinzuzufügen:
int fpm_event_set(struct fpm_event_s *ev, int fd, int flags, void (*callback)(struct fpm_event_s *, short, void *), void *arg) /* {{{ */{ if (!ev || !callback || fd < -1) { return -1; } memset(ev, 0, sizeof(struct fpm_event_s)); ev->fd = fd; ev->callback = callback; ev->arg = arg; ev->flags = flags; return 0; }/* }}} */
Fügen Sie dann diese Struktur fpm_event_s, die das ev im Code ist, zum hinzu fd fd reinhören.
Tatsächlich hängt dieser Additionsprozess auch mit verschiedenen asynchronen Modellen von fpm zusammen (alle implementiert durch die add-Methode, die fpm_event_module_s entspricht). Beispielsweise wird der gesamte ev-Parameter in data.ptr von epoll_event platziert. (Informationen zum Hinzufügen von Umfragen finden Sie unter )
Wenn alle FDS hinzugefügt sind (natürlich nicht nur signalbezogene FDS), können wir auf das Kommen des Ereignisses warten. (Epoll und Poll implementieren jeweils auch die Wait-Methode)
Okay, gehen Sie zurück zu sig_handler und schreiben Sie einen String 2 in sp[1]. Die Wartemethode empfängt das Signal, ruft das entsprechende Ereignis ab und ruft es auf:
static void fpm_got_signal(struct fpm_event_s *ev, short which, void *arg) /* {{{ */{ char c; int res, ret; int fd = ev->fd; do { res = read(fd, &c, 1); switch (c) { // 。。。。。。 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; }/* }}} */
Wenn Zeichenfolge 2 empfangen wird, führen Sie
fpm_pctl(FPM_PCTL_STATE_RELOADING, FPM_PCTL_ACTION_SET)
aus tatsächlich:
void fpm_pctl(int new_state, int action) /* {{{ */{ switch (action) { case FPM_PCTL_ACTION_SET : //。。。。。。 fpm_signal_sent = 0; fpm_state = new_state; zlog(ZLOG_DEBUG, "switching to '%s' state", fpm_state_names[fpm_state]); /* fall down */ case FPM_PCTL_ACTION_TIMEOUT : fpm_pctl_action_next(); break; //。。。。。 } }/* }}} */
Das heißt, nachdem fpm_state auf FPM_PCTL_STATE_RELOADING gesetzt wurde, gibt es keine Unterbrechung und die Ausführung wird fortgesetzt:
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); fpm_signal_sent = sig; fpm_pctl_timeout_set(timeout); } /* }}} */
Das heißt, das SIGQUIT-Signal wird an alle untergeordneten Prozesse gesendet.
Hier gibt es noch eine weitere, die später besprochen wird.
Der untergeordnete Prozess verarbeitet das Signal. Nachdem der übergeordnete Prozess das Senden des Signals abgeschlossen hat, ist der untergeordnete Prozess an der Reihe, es zu verarbeiten.
Der untergeordnete Prozess kann nur zur Verarbeitung an sig_soft_quit übergeben werden. Nachdem der untergeordnete Prozess initialisiert wurde, wird das SIGQUIT-Signal empfangen und von sig_soft_quit verarbeitet. Die letzte Aufrufverarbeitung:
void fcgi_terminate(void){ in_shutdown = 1; }
besteht darin, in_shutdown auf 1 zu setzen.
Der Schleifenkörper des untergeordneten Prozesses befindet sich in fcgi_accept_request, der weitere Beurteilungen in_shutdown enthält. Wenn er 1 ist, wird er direkt beendet:
Die Timeout-Verarbeitung wurde bereits erwähnt. Die folgenden Vorgänge wurden ausgeführt:
fpm_pctl(FPM_PCTL_STATE_UNSPECIFIED, FPM_PCTL_ACTION_TIMEOUT);
Unter dieser Bedingung wurde der untergeordnete Prozess direkt beendet.
Warum wird der Schlaf unterbrochen? Wir können sehen, dass das System den Schlaf aufruft (php_sleep ist ein Schlafmakro):
/* {{{ proto void sleep(int seconds) Delay for a given number of seconds */PHP_FUNCTION(sleep) { zend_longnum; if (zend_parse_parameters(ZEND_NUM_ARGS(), "l", &num) == FAILURE) { RETURN_FALSE; } php_sleep((unsigned int)num); }/* }}} */
Wenn die Schlaffunktion ausgeführt wird, ist der Status des Prozesses S:
interruptiblesleep Sobald es einen gibt Signal zu diesem Zeitpunkt Lösen Sie das Signal sofort aus und verarbeiten Sie es, wie z. B. das gerade erwähnte SIGQUIT. Nachdem es vorbei ist, wird festgestellt, dass der Ruhezustand ausgeführt wurde.
Weil ich geschrieben habe:
<b>sleep</b>() makesthecallingthreadsleepuntil <i>seconds</i> secondshave elapsedor a signalarriveswhichis not ignored.
Es sollte beachtet werden, dass, selbst wenn das Signal den Schlaf unterbricht, der Schlaf einfach übersprungen wird und die Ausführung fortgesetzt wird.
Das obige ist der detaillierte Inhalt vonDer Neuladevorgang von php-fpm. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!