Der Neuladevorgang von php-fpm

藏色散人
Freigeben: 2023-04-07 07:28:01
nach vorne
3968 Leute haben es durchsucht

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;
}/* }}} */
Nach dem Login kopieren

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;
}/* }}} */
Nach dem Login kopieren

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;
}/* }}} */
Nach dem Login kopieren

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 &#39;2&#39; :                  /* 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;
}/* }}} */
Nach dem Login kopieren

Wenn Zeichenfolge 2 empfangen wird, führen Sie

fpm_pctl(FPM_PCTL_STATE_RELOADING, FPM_PCTL_ACTION_SET)
Nach dem Login kopieren

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 &#39;%s&#39; state", fpm_state_names[fpm_state]); /* fall down */  case FPM_PCTL_ACTION_TIMEOUT :
fpm_pctl_action_next(); break; //。。。。。
}
}/* }}} */
Nach dem Login kopieren

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);
}
/* }}} */
Nach dem Login kopieren

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;
}
Nach dem Login kopieren

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);
Nach dem Login kopieren

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);
}/* }}} */
Nach dem Login kopieren

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.
Nach dem Login kopieren

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!

Verwandte Etiketten:
php
Quelle:juejin.im
Erklärung dieser Website
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn
Beliebte Tutorials
Mehr>
Neueste Downloads
Mehr>
Web-Effekte
Quellcode der Website
Website-Materialien
Frontend-Vorlage