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.
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 PHPVoici quelques signaux importants et couramment utilisés : 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 : 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 : . 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 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 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 La fonction pcntl_signal() installe un nouveau processeur de signal pour le signal spécifié par signo 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 La fonction pcntl_signal_dispatch() appelle. le processeur installé par chaque signal en attente via pcntl_signal() 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. 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 : 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 : 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 Avec la méthode pcntl_async_signals, il n'est pas nécessaire d'écrire une boucle infinie. 监听信号的包:
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
declare
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('test');//该条注册函数会被当成低级语句被执行
for($i=0;$i<=2;$i++){//for算一条低级语句
$i=$i;//赋值算一条
}
输出:六个“执行”
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”
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);
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.
<?php
pcntl_async_signals(true); // turn on async signals
pcntl_signal(SIGHUP, function($sig) {
echo "SIGHUP\n";
});
posix_kill(posix_getpid(), SIGHUP);
输出:
SIGHUP
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;
}
<?php
echo getmypid();//获取当前进程id
pcntl_signal(SIGUSR1,function(){
echo "触发信号用户自定义信号1";
});
while(1){
pcntl_signal_dispatch();
sleep(1);//死循环运行低级语句
}
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(¶m, 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, ¶m TSRMLS_CC);
zval_ptr_dtor(¶m);
zval_ptr_dtor(&retval);
}
next = queue->next;
queue->next = PCNTL_G(spares);
PCNTL_G(spares) = queue;
queue = next;
}
}
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!