Maison > développement back-end > tutoriel php > processus de rechargement php-fpm

processus de rechargement php-fpm

藏色散人
Libérer: 2023-04-07 07:28:01
avant
3990 Les gens l'ont consulté

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;
}/* }}} */
Copier après la connexion

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;
}/* }}} */
Copier après la connexion

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;
}/* }}} */
Copier après la connexion

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 &#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;
}/* }}} */
Copier après la connexion

Si la chaîne 2 est reçue, alors exécutez

fpm_pctl(FPM_PCTL_STATE_RELOADING, FPM_PCTL_ACTION_SET)
Copier après la connexion

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 &#39;%s&#39; state", fpm_state_names[fpm_state]); /* fall down */  case FPM_PCTL_ACTION_TIMEOUT :
fpm_pctl_action_next(); break; //。。。。。
}
}/* }}} */
Copier après la connexion

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);
}
/* }}} */
Copier après la connexion

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;
}
Copier après la connexion

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);
Copier après la connexion

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);
}/* }}} */
Copier après la connexion

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.
Copier après la connexion

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!

Étiquettes associées:
php
source:juejin.im
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal