Maison  >  Article  >  développement back-end  >  Une compréhension approfondie du traitement du signal du processus PHP dans un article

Une compréhension approfondie du traitement du signal du processus PHP dans un article

藏色散人
藏色散人avant
2023-04-01 07:30:032008parcourir

Cet article vous apporte des connaissances pertinentes sur PHP, qui présente principalement en détail le traitement du signal du processus PHP. Les amis intéressés peuvent y jeter un œil ensemble. J'espère que cela sera utile à tout le monde.

Une compréhension approfondie du traitement du signal du processus PHP dans un article

Contexte

Il y a deux semaines, mon patron m'a confié la tâche de rédiger un package de surveillance des signaux. Étant donné que les projets de notre entreprise s'exécutent dans des conteneurs, chaque fois qu'ils sont mis en ligne, ils doivent reconditionner l'image, puis la démarrer. Avant le reconditionnement, Dokcer enverra d'abord un signal au conteneur, puis attendra un délai d'attente (10 s par défaut) avant d'envoyer un signal SIGKILL pour terminer le conteneur. Il existe maintenant une situation où il y a un processus résident dans le conteneur. La tâche du processus est de consommer en permanence les messages dans la file d'attente. Supposons que vous souhaitiez vous connecter maintenant et que vous deviez arrêter le conteneur. Docker envoie un signal au processus résident exécuté dans le conteneur, lui indiquant que je vous arrêterai dans 10 secondes. Supposons que 9 secondes se soient écoulées et que le résident. le processus vient d'être retiré de la file d'attente. Pour un message, le processus a été tué avant que la logique suivante ne soit exécutée dans un délai d'une seconde. À ce moment, le message est perdu et des données sales peuvent être générées

Ce qui précède est. l'arrière-plan de cette tâche, qui doit être surveillée par le signal. Décidez quoi faire ensuite. Pour la situation ci-dessus, lorsque le processus résident reçoit le signal d'arrêt envoyé par Docker, le processus peut être bloqué et dormir jusqu'à ce que le conteneur soit tué. OK, après avoir clarifié le contexte, introduisons les signaux en PHP (je compilerai un article sur la façon d'écrire ce package plus tard et publierai le package sur https://packagist.org/ pour l'usage des amis qui en ont besoin) [ Étude recommandée :

Tutoriel vidéo PHP

]

1. Quels signaux existe-t-il dans le système d'exploitation Linux, parfois également appelés interruption logicielle. Un processus peut envoyer un signal à un autre processus. Par exemple, lorsqu'un processus enfant se termine, il enverra un SIGCHLD (signal n° 17) au processus parent pour avertir le processus parent, donc parfois les signaux sont également utilisés comme intermédiaire. mécanisme de communication de processus. Dans les systèmes Linux, nous utilisons généralement kill -9 XXPID pour terminer un processus. En fait, l'essence de cette commande est d'envoyer SIGKILL (signal n°9) à un processus. Pour les processus au premier plan, nous utilisons généralement le. Touche de raccourci Ctrl+c pour y mettre fin. Exécuter, l'essence de cette touche de raccourci est d'envoyer SIGINT (signal n° 2) au processus en cours, et le comportement par défaut du processus après avoir reçu ce signal est de terminer l'opération .

2. Signaux courants

Les signaux suivants peuvent être utilisés. Utilisez la commande kill -l pour afficher

Voici quelques signaux importants et couramment utilisés :

Nom du signal Valeur du signal Type de signal Description du signal
SIGHUP 1 Terminer le processus (raccrochage de la ligne du terminal) Ce signal est connecté au terminal utilisateur (normal ou non) Émis normalement et à la fin, généralement à la fin du processus de contrôle du terminal, notifiant chaque travail dans la même session, à ce moment-là, ils ne sont plus associés au terminal de contrôle
SIGQUIT 2 Terminer. le processus (interrompre le processus) Fin du programme (interruption, signal, émis lorsque l'utilisateur tape des caractères INTR (généralement Ctrl-C,
SIGQUIT 3 Créez un fichier CORE pour terminer le processus et générer un fichier CORE processus et génère un fichier CORE. SIGQUIT est similaire à SIGINT, mais est contrôlé par le caractère QUIT (généralement Ctrl-,). Lorsque le processus se termine en raison de la réception de SIGQUIT, il génère un fichier core dans celui-ci. sens, cela est similaire à un signal d'erreur de programme
SIGFPE 8 Créer un fichier CORE (exception à virgule flottante) SIGFPE est émis lorsqu'une erreur d'opération arithmétique fatale se produit. Il inclut non seulement les erreurs d'opération en virgule flottante, mais aussi toutes les autres erreurs arithmétiques telles que le débordement et la division par 0
SIGKILL 9 Terminer le processus (tuer le processus) SIGKILL est utilisé pour terminer immédiatement l'exécution du programme. Ce signal ne peut pas être bloqué. , traité et ignoré
SIGSEGV 11
SIGSEGV tente d'accéder à quelque chose qui ne lui est pas attribué en mémoire, ou essaie d'écrire des données sur une adresse mémoire qui n'a pas d'autorisation d'écriture
SIGALRM 14 Terminer le processus (la minuterie expire) Signal de synchronisation d'horloge SIGALRM, qui calcule l'heure réelle ou l'heure de l'horloge. La fonction utilise ce signal
SIGTERM 15 pour terminer le processus (signal de fin du logiciel). ) Fin du programme SIGTERM (terminer, signaler, contrairement à SIGKILL, ce signal peut être bloqué et traité. Il est généralement utilisé pour demander au programme lui-même de sortir normalement. La commande shell kill génère ce signal par défaut
SIGCHLD 17 Ignorer le signal (notifier le processus parent lorsque le processus enfant s'arrête ou se termine) SIGCHLD Lorsque le processus enfant se termine, le processus parent recevra ce signal
SIGVTALRM 26 Terminer le processus (virtuel timer expire) Signal d'horloge virtuelle SIGVTALRM Similaire à SIGALRM, mais calcule le temps CPU occupé par le processus
SIGIO 29 Ignorer le signal (les E/S sont possibles sur le descripteur) SIGIO Le le descripteur de fichier est prêt et les opérations d'entrée/sortie peuvent commencer

2. Fonctions liées au traitement du signal en PHP

L'extension pcntl de PHP et l'extension posix nous fournissent plusieurs méthodes pour faire fonctionner les signaux (si vous souhaitez utiliser ces fonctions, vous devez d'abord les installer Extension)

Ce qui suit est une introduction détaillée à plusieurs méthodes que j'ai utilisées dans cette tâche :

declare

La structure de déclaration est utilisée pour définir les instructions d'exécution d'un morceau de code. La syntaxe de declare est similaire à celle des autres structures de contrôle de flux. La partie directive vous permet de définir le comportement du segment de code de déclaration. Actuellement, seules deux commandes sont reconnues :

tiques et encodage

. La partie instruction dans le segment de code de déclaration sera exécutée - la manière dont elle est exécutée et les effets secondaires qui se produisent pendant l'exécution dépendent des instructions définies dans la directive

Ticks

Tick (cycle d'horloge) est un interprète dans le code de déclaration segment La valeur de l'événement N qui se produira à chaque fois que N

instructions de bas niveau

chronométrées sont exécutées est spécifiée avec ticks=N dans la partie directive de declare. Toutes les déclarations ne peuvent pas être chronométrées. Généralement, les expressions conditionnelles et les expressions paramétriques ne sont pas chronométrées. Les événements qui apparaissent dans chaque tick sont spécifiés par register_tick_function(). Notez que plusieurs événements peuvent apparaître dans chaque tick. Pour un contenu plus détaillé, veuillez consulter la documentation officielle : https://www.php.net/manual/zh/control-structures.declare.php

declare (directive)
    statement

pcntl_signalpcntl_signal, installez un processeur de signal

<?php
declare(ticks=1);//每执行一条时,触发register_tick_function()注册的函数
$a=1;//在注册之前,不算
function test(){//定义一个函数
    echo "执行\n";
}
register_tick_function(&#39;test&#39;);//该条注册函数会被当成低级语句被执行
for($i=0;$i<=2;$i++){//for算一条低级语句
    $i=$i;//赋值算一条
}
输出:六个“执行”

La fonction pcntl_signal() installe un nouveau processeur de signal pour le signal spécifié par signo

pcntl_signal ( int $signo , callback $handler [, bool $restart_syscalls = true ] ) : bool

posix_killposix_kill, envoie un signal au processus

declare(ticks = 1);
pcntl_signal(SIGINT,function(){
    echo "你按了Ctrl+C".PHP_EOL;
});
while(1){
    sleep(1);//死循环运行低级语句
}
输出:当按Ctrl+C之后,会输出“你按了Ctrl+C”

Le premier paramètre est l'ID du processus, et le premier paramètre est l'ID du processus. Les deux paramètres sont le signal que vous souhaitez envoyer

posix_kill ( int $pid , int $sig ) : bool

pcntl_signal_dispatchpcntl_signal_dispatch, qui appelle le processeur en attente du signal

a.php
<?php
declare(ticks = 1);
echo getmypid();//获取当前进程id
pcntl_signal(SIGINT,function(){
    echo "你给我发了SIGINT信号";
});
while(1){
    sleep(1);
}

b.php
<?php
posix_kill(执行1.php时输出的进程id, SIGINT);

La fonction pcntl_signal_dispatch() appelle. le processeur installé par chaque signal en attente via pcntl_signal()

pcntl_signal_dispatch ( void ) : bool

pcntl_async_signals()Traitement du signal asynchrone, utilisé pour activer le traitement du signal asynchrone sans ticks (ce qui entraîne beaucoup de surcharge supplémentaire). (PHP>=7.1)

<?php
echo "安装信号处理器...\n";
pcntl_signal(SIGHUP,  function($signo) {
     echo "信号处理器被调用\n";
});
echo "为自己生成SIGHUP信号...\n";
posix_kill(posix_getpid(), SIGHUP);
echo "分发...\n";
pcntl_signal_dispatch();
echo "完成\n";
?>

输出:
安装信号处理器...
为自己生成SIGHUP信号...
分发...
信号处理器被调用
完成

3. Comment gérer les sémaphores en PHPNous savons avant que nous pouvons surveiller le signal grâce à la combinaison de declare(ticks=1) et pcntl_signal, c'est-à-dire, chaque instruction PHP de bas niveau vérifiera s'il y a des signaux non gérés dans le processus en cours, ce qui est en fait très consommateur de performances.

Le principe d'implémentation de pcntl_signal est d'ajouter d'abord le signal à une file d'attente après avoir déclenché le signal. Vérifiez ensuite en permanence s'il y a un signal dans la fonction de rappel des ticks de PHP. S'il y a un signal, exécutez la fonction de rappel spécifiée dans PHP. Sinon, quittez la fonction.

<?php
pcntl_async_signals(true); // turn on async signals

pcntl_signal(SIGHUP,  function($sig) {
    echo "SIGHUP\n";
});

posix_kill(posix_getpid(), SIGHUP);

输出:
SIGHUP

Après PHP5.3, il y a la fonction pcntl_signal_dispatch. À ce stade, la déclaration n'est plus nécessaire. Il vous suffit d'ajouter la fonction dans la boucle pour appeler le signal et transmettre :

PHP_MINIT_FUNCTION(pcntl)
{
 php_register_signal_constants(INIT_FUNC_ARGS_PASSTHRU);
 php_pcntl_register_errno_constants(INIT_FUNC_ARGS_PASSTHRU);
 php_add_tick_function(pcntl_signal_dispatch TSRMLS_CC);

 return SUCCESS;
}

Tout le monde sait que le ticks=1 de PHP signifie que cette fonction sera rappelée à chaque fois qu'une ligne de Le code PHP est exécuté. En fait, aucun signal n'est généré la plupart du temps, mais la fonction ticks est toujours exécutée. Si un programme serveur reçoit 1 000 requêtes en 1 seconde, chaque requête exécutera en moyenne 1 000 lignes de code PHP. Ensuite, pcntl_signal de PHP apporte 1 000 * 1 000 supplémentaires, soit 1 million d'appels de fonction vides. Cela gaspillera beaucoup de ressources CPU. Une meilleure approche consiste à supprimer les coches et à utiliser à la place pcntl_signal_dispatch pour gérer vous-même le signal dans la boucle de code. Implémentation de la fonction pcntl_signal_dispatch :

<?php
echo getmypid();//获取当前进程id
pcntl_signal(SIGUSR1,function(){
    echo "触发信号用户自定义信号1";
});
while(1){
    pcntl_signal_dispatch();
    sleep(1);//死循环运行低级语句
}

Mais ce qui est dégoûtant avec celle ci-dessus, c'est qu'elle doit être placée dans une boucle infinie. Après PHP7.1, une fonction qui complète la réception et le traitement asynchrone du signal est sortie : pcntl_async_signals

void pcntl_signal_dispatch()
{
 //.... 这里略去一部分代码,queue即是信号队列
 while (queue) {
  if ((handle = zend_hash_index_find(&PCNTL_G(php_signal_table), queue->signo)) != NULL) {
   ZVAL_NULL(&retval);
   ZVAL_LONG(&param, queue->signo);

   /* Call php signal handler - Note that we do not report errors, and we ignore the return value */
   /* FIXME: this is probably broken when multiple signals are handled in this while loop (retval) */
   call_user_function(EG(function_table), NULL, handle, &retval, 1, &param TSRMLS_CC);
   zval_ptr_dtor(&param);
   zval_ptr_dtor(&retval);
  }
  next = queue->next;
  queue->next = PCNTL_G(spares);
  PCNTL_G(spares) = queue;
  queue = next;
 }
}

Avec la méthode pcntl_async_signals, il n'est pas nécessaire d'écrire une boucle infinie.

监听信号的包:

https://github.com/Rain-Life/monitorSignal

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!

Déclaration:
Cet article est reproduit dans:. en cas de violation, veuillez contacter admin@php.cn Supprimer