Le contenu de cet article est de partager avec vous une explication détaillée de l'écriture de démons pour la programmation système en PHP. Il a une certaine valeur de référence. Les amis dans le besoin peuvent se référer à
(1) Processus. Groupe, concepts tels que session, terminal de contrôle et processus de contrôle
Groupe de processus : Chaque processus a un groupe de processus (groupe de processus) auquel il appartient, et le groupe de processus a un chef de processus (chef de groupe de processus), l'ID de groupe de processus est le numéro de processus du chef de groupe de processus, donc pour déterminer si un processus est le chef de groupe de processus, il vous suffit de comparer si le nom du processus est égal à son processus identifiant de groupe. Vous pouvez l'utiliser en PHP. La fonction posix_getpgrp() obtient l'ID de groupe de processus du processus actuel, et posix_getpid() est utilisée pour obtenir l'ID de processus du processus actuel.
<?php function isGroupLeader() { return posix_getpgrp() == posix_getpid(); } $pid = pcntl_fork(); if($pid == 0) { echo '子进程:' . PHP_EOL; } elseif($pid > 0) { sleep(2); echo '父进程:' . PHP_EOL; } echo "当前进程组gid:" . posix_getpgrp() . PHP_EOL; echo "当前进程号pid:" . posix_getpid() . PHP_EOL; if (isGroupLeader()) { echo 'Is a process group leader' . PHP_EOL; } else { echo 'Is not a process group leader' . PHP_EOL; }
La routine ci-dessus affichera :
子进程: 当前进程组gid:15827 当前进程号pid:15828 Is not a process group leader 父进程: 当前进程组gid:15827 当前进程号pid:15827 Is a process group leader
Session : Une session est une collection de plusieurs groupes de processus. Un groupe de processus dans la session est le leader de la session, et l'ID de session est l'identifiant du groupe de processus du leader de la session. fonction posix_getsid(int $pid) pour obtenir l'identifiant de session du processus spécifié, ou vous pouvez utiliser la fonction posix_setsid() pour créer une nouvelle session. À ce stade, le processus devient le leader de la nouvelle session. renvoie avec succès l'ID de session nouvellement créé, ou -1 en cas d'échec Notez que posix_setsid() est appelé sous Linux. Le processus de la fonction ne peut pas être le chef du groupe de processus , sinon l'appel échouera car un processus dans un groupe de processus ne peut pas s'étendre sur plusieurs sessions en même temps .
Introduction au document sur setsid sous Linux :
setsid() creates a new session if the calling process is not a process group leader. The calling process is the leader of the new session, the process group leader of the new process group, and has no controlling tty. The process group ID and session ID of the calling process are set to the PID of the calling process. The calling process will be the only process in this new process group and in this new session.
<?php function isGroupLeader() { return posix_getpgrp() == posix_getpid(); } echo "当前会话id: " . posix_getsid(0) . PHP_EOL; //传0表示获取当前进程的会话id if (isGroupLeader()) { echo "当前进程是进程组长\n"; } $ret = posix_setsid(); //创建一个新会话 var_dump($ret); //由于当前进程是进程组长,此处会返回-1, 表示调用失败
La routine ci-dessus affichera :
当前会话id: 13000 当前进程是进程组长 int(-1)
Alors comment créer une nouvelle session Nous avons remarqué qu'après avoir utilisé pcntl_fork() pour créer un processus enfant, ce Le Le processus enfant n'est pas le leader du processus, vous pouvez donc utiliser le processus enfant pour créer une nouvelle session.
<?php function isGroupLeader() { return posix_getpgrp() == posix_getpid(); } echo "当前进程所属会话ID:" . posix_getsid(0) . PHP_EOL; $pid = pcntl_fork(); if ($pid > 0) { exit(0); // 让父进程退出 } elseif ($pid == 0) { if (isGroupLeader()) { echo "是进程组组长\n"; } else { echo "不是进程组组长\n"; } echo "进程组ID:" . posix_getpgrp() . PHP_EOL; echo "进程号pid: " . posix_getpid() . PHP_EOL; $ret = posix_setsid(); var_dump($ret); echo "当前进程所属会话ID:" . posix_getsid(0) . PHP_EOL; }
La routine ci-dessus affichera :
当前进程所属会话ID:13000 [root@localhost php]# 不是进程组组长 进程组ID:15856 进程号pid: 15857 int(15857) 当前进程所属会话ID:15857
Une nouvelle session a été créée avec succès à l'aide du processus enfant.
Terminal de contrôle et Processus de contrôle : (Le terminal est le terme général désignant tous les périphériques d'entrée et de sortie, tels que le clavier, la souris et le moniteur. un terminal) Une session Il peut y avoir un terminal de contrôle, et un terminal de contrôle est exclusif à une session. Lors de la première création de la session, il n'y a pas de terminal de contrôle, mais le responsable de la session peut demander à ouvrir un terminal. Si ce terminal n'est pas le terminal de contrôle des autres sessions, le terminal deviendra à ce moment le terminal de contrôle de la session. Le leader de la session est appelé le processus de contrôle.
Pour déterminer si une session a un terminal de contrôle sous Linux, on peut essayer d'ouvrir un fichier spécial /dev/tty, qui pointe vers le Véritable terminal de contrôle, s'il est ouvert avec succès, cela signifie que vous avez un terminal de contrôle, sinon, il n'y a pas de terminal de contrôle.
<?php function isGroupLeader() { return posix_getpgrp() == posix_getpid(); } $pid = pcntl_fork(); if ($pid > 0) { sleep(1); $fp = fopen("/dev/tty", "rb"); if ($fp) { echo "父进程会话 " . posix_getsid(0) . " 拥有控制终端\n"; } else { echo "父进程会话 " . posix_getsid(0) . " 不拥有控制终端\n"; } exit(0); // 让父进程退出 } elseif ($pid == 0) { if (isGroupLeader()) { echo "是进程组组长\n"; } else { echo "不是进程组组长\n"; } $ret = posix_setsid(); var_dump($ret); $fp = fopen("/dev/tty", "rb"); if ($fp) { echo "子进程会话 " . posix_getsid(0) . " 拥有控制终端\n"; } else { echo "子进程会话 " . posix_getsid(0) . " 不拥有控制终端\n"; } }
Le processus enfant de la routine ci-dessus crée une nouvelle session, puis les processus parent et enfant tentent d'ouvrir le fichier /dev/tty. de la routine est la suivante :
不是进程组组长 int(15906) PHP Warning: fopen(/dev/tty): failed to open stream: No such device or address in /root/php/setsid.php on line 30 Warning: fopen(/dev/tty): failed to open stream: No such device or address in /root/php/setsid.php on line 30 子进程会话 15906 不拥有控制终端 父进程会话 13000 拥有控制终端
Générer le signal SIGHUP
1 . Lorsqu'une session perd le contrôle du terminal, le noyau Le processus de contrôle de la session envoie un signal SIGHUP, et généralement le processus de contrôle de la session est le processus shell. Lorsque le shell reçoit un signal SIGHUP, il enverra également un. Signal SIGHUP à tous les groupes de processus (groupes de processus de premier plan ou d'arrière-plan) créés par celui-ci, puis quittez. La méthode de traitement par défaut pour un processus recevant un signal SIGHUP consiste à quitter le processus. Bien sûr, le processus peut également personnaliser le traitement du signal ou. ignorez-le.
2. De plus, lorsque le processus de contrôle se termine, le noyau enverra également un signal SIGHUP à tous les membres du groupe de processus de premier plan du terminal.
<?php $callback = function($signo){ $sigstr = 'unkown signal'; switch($signo) { case SIGINT: $sigstr = 'SIGINT'; break; case SIGHUP: $sigstr = 'SIGHUP'; break; case SIGTSTP: $sigstr = 'SIGTSTP'; break; } file_put_contents("daemon.txt", "catch signal $sigstr\n", FILE_APPEND); }; pcntl_signal(SIGINT, $callback); pcntl_signal(SIGHUP, $callback); pcntl_signal(SIGTSTP, $callback); while(1) { sleep(100); pcntl_signal_dispatch(); }
Utilisez php sighup.php pour exécuter le programme, puis fermez directement le terminal et reconnectez-vous au shell. Vous constaterez que le programme est toujours en cours d'exécution. , fichier daemon.txt Le signal SIGHUP capturé sera enregistré.
[root@localhost php]# cat daemon.txt catch signal SIGHUP [root@localhost php]# ps aux | grep sighup.php root 18438 0.0 0.4 191600 8996 ? S 16:48 0:00 php sighup.php root 18443 0.0 0.0 103328 896 pts/0 S+ 16:53 0:00 grep sighup.php
En même temps, Linux fournit une commande nohup, qui permet au processus d'ignorer tous les signaux SIGHUP, tels que
[root@localhost php]# nohup php sighup.php nohup: 忽略输入并把输出追加到"nohup.out"
(2) Entrée standard, sortie standard, sortie d'erreur standard
Il existe trois descripteurs de fichiers ouverts par défaut dans php STDIN, STDOUT et STDERR correspondent respectivement aux trois descripteurs de fichiers ci-dessus Puisque l'entrée et la sortie standard sont liées au terminal, elles ne sont d'aucune utilité au processus démon. Elles peuvent être fermées directement, mais en les fermant. directement peut causer un problème. Veuillez consulter le code suivant
<?php fclose(STDOUT); $fp = fopen("stdout.log", "a"); echo "hello world\n";
Lors de l'exécution du code ci-dessus, l'écran n'affichera pas d'informations d'écho, mais sera écrit à l'air libre. Cela est dû à l'arrêt. Après le descripteur de fichier STDOUT, le descripteur de fichier correspondant est libéré et Linux utilise toujours le plus petit descripteur de fichier disponible pour ouvrir un fichier, donc ce descripteur de fichier pointe désormais vers le fichier ouvert par fopen, provoquant le les informations initialement écrites sur la sortie standard doivent maintenant être écrites. Arrivées dans le fichier. Afin d'éviter ce comportement étrange, nous pouvons immédiatement ouvrir le fichier trou noir /dev/null fourni par Linux après avoir fermé ces trois descripteurs de fichiers, tels que :
<?php fclose(STDIN); fclose(STDOUT); fclose(STDERR); fopen('/dev/null', 'r'); fopen('/dev/null', 'w'); fopen('/dev/null', 'w'); $fp = fopen("stdout.log", "a"); echo "hello world\n";
上面这个例程关闭STDIN,STDOUT, STDERR立马打开 /dev/null 三次,这样echo的信息会直接写到黑洞中,避免了前面出现的怪异的问题。
(三)编写守护进程涉及的其他问题
编写守护进程还涉及工作目录、文件掩码、信号处理、热更新、安全的启动停止等等问题,这里先留给大家自己百度,后期有空再来补充。
(四)一个守护进程的示例
<?php //由于进程组长无法创建会话,fork一个子进程并让父进程退出,以便可以创建新会话 switch(pcntl_fork()) { case -1: exit("fork error"); break; case 0: break; default: exit(0); //父进程退出 } posix_setsid(); //创建新会话,脱离原来的控制终端 //再次fork并让父进程退出, 子进程不再是会话首进程,让其永远无法打开一个控制终端 switch(pcntl_fork()) { case -1: exit("fork error"); break; case 0: break; default: exit(0); //父进程退出 } //关闭标准输入输出 fclose(STDIN); fclose(STDOUT); fclose(STDERR); fopen('/dev/null', 'r'); fopen('/dev/null', 'w'); fopen('/dev/null', 'w'); //切换工作目录 chdir('/'); //清除文件掩码 umask(0); //由于内核不会再为进程产生SIGHUP信号,我们可以使用该信号来实现热重启 pcntl_signal(SIGHUP, function($signo){ //重新加载配置文件,重新打开日志文件等等 }); for(;;) { pcntl_signal_dispatch(); //处理信号回调 //实现业务逻辑 }
to be continue!
相关推荐:
PHP实现系统编程之本地套接字(Unix Domain Socket)
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!