Résumé : le rechargement entraînera la fin prématurée du sommeil, nous avons donc exploré comment implémenter l'opération de rechargement de fpm ?
Le processus de rechargement de php-fpm
Cet article est analysé sous PHP7.0 fpm, et le paramètre process_control_timeout n'est pas 0.
Signal de redémarrage
Tout d'abord, nous pouvons savoir que l'opération de rechargement de fpm envoie en fait le signal USR2 au processus fpm.
Dans le processus maître de fpm, la fonction de traitement du signal est enregistrée par :
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; }/* }}} */
En bref, tous les signaux sont définis pour bloquer par , puis la fonction de traitement du signal correspondante est définie par sigaction .
Lorsque nous rechargeons fpm, systemctl envoie le signal USR2 au processus maître de fpm et exécute la fonction :
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; }/* }}} */
Le point clé est zend_quiet_write, qui est . La fonction sig_handler écrit une chaîne 2 dans sp[1].
Il convient de noter ici que sp[0] et sp[1] sont des sockets locaux créés par .
Le maître commence à redémarrer. La fonction de traitement du signal précédente sera appelée lorsque le signal se produira, mais la logique principale du programme ne sera toujours pas perturbée. Alors, comment le processus maître fpm sait-il recharger ?
La réponse réside dans , qui est la boucle événementielle du processus maître.
Avant de boucler, nous devons utiliser sp[0] pour ajouter une struct fpm_event_s au fd d'écoute :
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; }/* }}} */
Ajoutez ensuite cette struct fpm_event_s, qui est l'ev dans le code, au j'écoute fd fd in.
En fait, ce processus d'ajout est également lié à différents modèles asynchrones de fpm (tous implémentés par la méthode add correspondant à fpm_event_module_s. Par exemple, l'intégralité du paramètre ev est placé dans le data.ptr de epoll_event). (Pour l'ajout d'un sondage, veuillez vous référer à )
Lorsque tous les fds sont ajoutés (bien sûr pas seulement les fds liés au signal), nous pouvons utiliser pour attendre que l'événement arrive. (epoll et poll implémentent également respectivement la méthode wait)
D'accord, retournez à sig_handler et écrivez une chaîne 2 dans sp[1]. La méthode wait reçoit le signal, obtient l'ev correspondant et l'appelle. En fait, elle est appelée, ce qui est :
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; }/* }}} */
Si la chaîne 2 est reçue, alors exécutez
fpm_pctl(FPM_PCTL_STATE_RELOADING, FPM_PCTL_ACTION_SET)
qui est. en fait :
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; //。。。。。 } }/* }}} */
C'est-à-dire qu'après avoir défini fpm_state sur FPM_PCTL_STATE_RELOADING, il n'y a pas d'interruption et l'exécution continue :
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); } /* }}} */
C'est-à-dire l'envoi du signal SIGQUIT à tous les processus enfants.
Il y en a un autre ici, dont nous parlerons plus tard.
Le processus enfant gère le signal. Une fois que le processus parent a fini d'envoyer le signal, c'est au tour du processus enfant de le traiter.
Le processus enfant ne peut être transmis qu'à sig_soft_quit pour traitement. Une fois le processus enfant initialisé, le signal SIGQUIT est reçu, qui est traité par sig_soft_quit. Le traitement de l'appel final :
void fcgi_terminate(void){ in_shutdown = 1; }
consiste à définir in_shutdown sur 1.
Le processus enfant se termine. Le corps de la boucle du processus enfant est dans fcgi_accept_request, qui contient plus de jugement dans_shutdown S'il vaut 1, il se terminera directement :
Le traitement du délai d'attente est mentionné plus tôt. Les opérations suivantes ont été effectuées :
fpm_pctl(FPM_PCTL_STATE_UNSPECIFIED, FPM_PCTL_ACTION_TIMEOUT);
Dans cette condition, , a directement quitté le processus enfant.
Pourquoi le sommeil est-il interrompu ? On peut voir que le système appelle sleep (php_sleep est une macro de sleep) :
/* {{{ 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); }/* }}} */
Lorsque la fonction sleep est exécutée, le statut du processus est S :
interruptiblesleep Une fois qu'il y a un signal à ce moment-là Déclenchez et traitez le signal immédiatement, comme le SIGQUIT que nous venons de mentionner. Une fois terminé, il s'avère que le sommeil a été exécuté.
Parce que j'ai écrit :
<b>sleep</b>() makesthecallingthreadsleepuntil <i>seconds</i> secondshave elapsedor a signalarriveswhichis not ignored.
Il convient de noter que, même si le signal interrompt le sommeil, il saute simplement le sommeil et continue l'exécution.
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!