Le contenu de cet article est de partager avec vous l'introduction de la programmation multi-processus et des processus orphelins et zombies dans la programmation système PHP. Il a une certaine valeur de référence. Les amis dans le besoin peuvent s'y référer
. La programmation multi-processus est également un aspect important de la programmation système, mais les programmeurs PHP n'ont généralement pas besoin de se soucier des problèmes multi-processus, car le serveur Web ou PHP-FPM a déjà géré les problèmes de processus pour nous, mais si nous voulons utiliser PHP Lors du développement de programmes CLI, la programmation multi-processus est une technologie de base indispensable.
La méthode de contrôle de processus en PHP utilise principalement l'extension PCNTL (Process Control). Par conséquent, avant d'effectuer une programmation multi-processus, vous devez d'abord vous assurer que votre PHP dispose de la dernière version de PCNTL. Extensions, vous pouvez saisir la commande php -m pour afficher les extensions actuellement installées :
Cette extension nous fournit un ensemble de méthodes pour les opérations de processus :
PCNTL 函数 pcntl_alarm — 为进程设置一个alarm闹钟信号 pcntl_errno — 别名 pcntl_get_last_error pcntl_exec — 在当前进程空间执行指定程序 pcntl_fork — 在当前进程当前位置产生分支(子进程)。 pcntl_get_last_error — Retrieve the error number set by the last pcntl function which failed pcntl_getpriority — 获取任意进程的优先级 pcntl_setpriority — 修改任意进程的优先级 pcntl_signal_dispatch — 调用等待信号的处理器 pcntl_signal_get_handler — Get the current handler for specified signal pcntl_signal — 安装一个信号处理器 pcntl_sigprocmask — 设置或检索阻塞信号 pcntl_sigtimedwait — 带超时机制的信号等待 pcntl_sigwaitinfo — 等待信号 pcntl_strerror — Retrieve the system error message associated with the given errno pcntl_wait — 等待或返回fork的子进程状态 pcntl_waitpid — 等待或返回fork的子进程状态 pcntl_wexitstatus — 返回一个中断的子进程的返回代码 pcntl_wifexited — 检查状态代码是否代表一个正常的退出。 pcntl_wifsignaled — 检查子进程状态码是否代表由于某个信号而中断 pcntl_wifstopped — 检查子进程当前是否已经停止 pcntl_wstopsig — 返回导致子进程停止的信号 pcntl_wtermsig — 返回导致子进程中断的信号
pcntl_fork — Génère une branche (enfant) à la position actuelle du processus actuel). Annotation : fork crée un processus enfant, un processus parent et un processus enfant Ils commencent tous à partir de la position fork et continuent vers le bas. La différence est que lors de l'exécution du processus parent, la valeur de retour fork obtenue est le numéro du processus enfant et le processus enfant obtient 0.
Le processus enfant dupliqué est presque une copie complète du processus parent. Les processus parent et enfant partagent des segments de code, bien que le segment de données, le tas et la pile. Les processus parent et enfant sont indépendants les uns des autres, mais au début, le processus enfant copie complètement les données du processus parent, mais les modifications ultérieures ne s'affectent pas.
int pcntl_fork ( void )
Créer une démonstration de code de 5 sous-processus :
<?php for($i = 0; $i < 5; $i++) { $pid = pcntl_fork(); //创建子进程,子进程也是从这里开始执行。 if ($pid == 0) { break; //由于子进程也会执行循环的代码,所以让子进程退出循环,否则子进程又会创建自己的子进程。 } } sleep($i); //第一个创建的子进程将睡眠0秒,第二个将睡眠1s,依次类推...主进程会睡眠5秒 if ($i < 5) { exit("第 " . ($i+1) . " 个子进程退出..." . time() . PHP_EOL); } else { exit("父进程退出..." . time() . PHP_EOL); }
Exécuter le résultat :
[root@localhost process]# php process.php 第 1 个子进程退出...1503322773 第 2 个子进程退出...1503322774 第 3 个子进程退出...1503322775 第 4 个子进程退出...1503322776 第 5 个子进程退出...1503322777 父进程退出...1503322778
Il est important de comprendre la fonction pcntl_fork : "fork crée un processus enfant, le parent Le processus et le processus enfant continuent de s'exécuter à partir de la position de fork. La différence est que pendant l'exécution du processus parent, la valeur de retour du fork est le numéro du processus enfant et le processus enfant obtient 0"
Modifiez légèrement le code ci-dessus pour empêcher le processus de se terminer, puis utilisez la commande ps pour vérifier l'état du système :
<?php for($i = 0; $i < 5; $i++) { $pid = pcntl_fork(); if ($pid == 0) { break; //由于子进程也会执行循环的代码,所以让子进程退出循环 } } sleep($i); //第一个创建的子进程将睡眠0秒,第二个将睡眠1s,依次类推...主进程会睡眠5秒 /* if ($i < 5) { exit("第 " . ($i+1) . " 个子进程退出..." . time() . PHP_EOL); } else { exit("父进程退出..." . time() . PHP_EOL); } */ while(1) { sleep(1); //执行死循环不退出 }
Après l'exécution, entrez ps -ef grep php pour afficher le processus système
[root@localhost ~]# ps -ef | grep php root 3670 3609 0 21:54 pts/0 00:00:00 php process.php root 3671 3670 0 21:54 pts/0 00:00:00 php process.php root 3672 3670 0 21:54 pts/0 00:00:00 php process.php root 3673 3670 0 21:54 pts/0 00:00:00 php process.php root 3674 3670 0 21:54 pts/0 00:00:00 php process.php root 3675 3670 0 21:54 pts/0 00:00:00 php process.php root 3677 3646 0 21:54 pts/1 00:00:00 grep php
Vous pouvez voir 6 processus php process.php, la deuxième colonne est le processus et la troisième colonne est le processus parent du numéro de processus, vous pouvez voir que les numéros de processus parents des cinq processus suivants sont tous les numéros de processus du premier processus.
Le code ci-dessus, le processus enfant et le processus parent exécutent le même code. Existe-t-il un moyen de faire en sorte que le processus enfant et le processus parent. faire des choses différentes ? , le moyen le plus simple est de juger si le processus enfant exécute le code du processus enfant et si le processus parent exécute le code du processus parent :
<?php $ppid = posix_getpid(); //记录父进程的进程号 for($i = 0; $i < 5; $i++) { $pid = pcntl_fork(); if ($pid == 0) { break; //由于子进程也会执行循环的代码,所以让子进程退出循环 } } if ($ppid == posix_getpid()) { //父进程 while(1) { sleep(1); } } else { //子进程 for($i = 0; $i < 100; $i ++) { echo "子进程" . posix_getpid() . " 循环 $i ...\n"; sleep(1); } }
[root@localhost process]# php process.php 子进程6677 循环 0 ... 子进程6676 循环 0 ... 子进程6678 循环 0 ... 子进程6680 循环 0 ... 子进程6679 循环 0 ... 子进程6677 循环 1 ... 子进程6676 循环 1 ... 子进程6678 循环 1 ... 子进程6680 循环 1 ... 子进程6679 循环 1 ... 子进程6677 循环 2 ... 子进程6676 循环 2 ... 子进程6678 循环 2 ... 子进程6680 循环 2 ...
En fait, les processus parent et enfant du programme ci-dessus exécutent toujours le même code, mais les branches if les entrées sont différentes, et pcntl_exec peut complètement séparer le processus enfant de l'influence du processus parent, pour implémenter de nouvelles procédures.
pcntl_exec — 在当前进程空间执行指定程序
void pcntl_exec ( string $path [, array $args [, array $envs ]] )
path
path必须时可执行二进制文件路径或一个在文件第一行指定了 一个可执行文件路径标头的脚本(比如文件第一行是#!/usr/local/bin/perl的perl脚本)。 更多的信息请查看您系统的execve(2)手册。
args
args是一个要传递给程序的参数的字符串数组。
envs
envs是一个要传递给程序作为环境变量的字符串数组。这个数组是 key => value格式的,key代表要传递的环境变量的名称,value代表该环境变量值。
注意该方法的返回值比较特殊:当发生错误时返回 FALSE ,没有错误时没有返回,因为pcntl_exec调用成功,子进程就去运行新的程序 从父进程继承的代码段、数据段、堆、栈等信息全部被替换成新的,此时的pcntl_exec函数调用栈已经不存在了,所以也就没有返回了。代码示例:
<?php for($i = 0; $i < 3; $i++) { $pid = pcntl_fork(); if($pid == 0) { echo "子进程pid = " . posix_getpid() . PHP_EOL; $ret = pcntl_exec('/bin/ls'); //执行 ls 命令, 此处调用成功子进程将不会再回来执行下面的任何代码 var_dump($ret); // 此处的代码不会再执行 } } sleep(5); //睡眠5秒以确保子进程执行完毕,原因后面会说 exit( "主进程退出...\n");
运行结果:
[root@localhost process]# php pcntl_exec.php 子进程pid = 6728 子进程pid = 6729 子进程pid = 6727 pcntl_exec.php process.php pcntl_exec.php process.php pcntl_exec.php process.php 主进程退出... [root@localhost process]# ls pcntl_exec.php process.php
以上就是对PHP多进程开发的简单介绍,对于子进程不同的存续状态,引出孤儿进程和僵尸进程的概念,在linux系统中,init进程(1号进程)是所有进程的祖先,其他进程要么是该进程的子进程,要么是子进程的子进程,子进程的子进程的子进程...,linux系统中可以用 pstree 命令查看进程树结构:
在多进程程序中,如果父进程先于子进程退出,那么子进程将会被init进程收养,成为init进程的子进程,这种进程被称为孤儿进程,我们可以把上面的代码稍作修改来演示这种情况:
<?php $ppid = posix_getpid(); //记录父进程的进程号 for($i = 0; $i < 5; $i++) { $pid = pcntl_fork(); if ($pid == 0) { break; //由于子进程也会执行循环的代码,所以让子进程退出循环 } } if ($ppid == posix_getpid()) { //父进程直接退出,它的子进程都会成为孤儿进程 exit(0); } else { //子进程 for($i = 0; $i < 100; $i ++) { echo "子进程" . posix_getpid() . " 循环 $i ...\n"; sleep(1); } }
运行该程序,然后查看进程状态:
[root@localhost ~]# ps -ef | grep php root 2903 1 0 12:09 pts/0 00:00:00 php pcntl.fork.php root 2904 1 0 12:09 pts/0 00:00:00 php pcntl.fork.php root 2905 1 0 12:09 pts/0 00:00:00 php pcntl.fork.php root 2906 1 0 12:09 pts/0 00:00:00 php pcntl.fork.php root 2907 1 0 12:09 pts/0 00:00:00 php pcntl.fork.php root 2935 2912 0 12:10 pts/1 00:00:00 grep php
可以看到五个子进程的父进程号都是1了,并且这时控制台不再被程序占用,子进程转到了后台运行,这种孤儿进程被init进程收养的机制是实现后面将要介绍的守护进程的必要条件之一。
子进程还有一种状态叫僵尸进程,子进程结束时并不是完全退出,内核进程表中仍旧保有该进程的记录,这样做的目的是能够让父进程可以得知子进程的退出状态,以及子进程是自杀(调用exit或代码执行完毕)还是他杀(被信号终止),父进程可以调用pcntl_wait 或 pcntl_waitpid 方法来回收子进程(收尸),释放子进程占用的所有资源,并获得子进程的退出状态,如果父进程不做回收,则僵尸进程一直存在,如果这时父进程也退出了,则这些僵尸进程会被init进程接管并自动回收。
对于linux系统来说,一个长时间运行的多进程程序一定要回收子进程,因为系统的进程资源是有限的,僵尸进程会让系统的可用资源减少。
代码演示僵尸进程的产生:
<?php $ppid = posix_getpid(); //记录父进程的进程号 for($i = 0; $i < 5; $i++) { $pid = pcntl_fork(); if ($pid == 0) { break; //由于子进程也会执行循环的代码,所以让子进程退出循环 } } if ($ppid == posix_getpid()) { //父进程不退出,也不回收子进程 while(1) { sleep(1); } } else { //子进程退出,会成为僵尸进程 exit("子进程退出 $ppid ...\n"); }
运行之后查看进程状态:
[root@localhost ~]# ps -ef | grep php root 2971 2864 0 14:13 pts/0 00:00:00 php pcntl.fork.php root 2972 2971 0 14:13 pts/0 00:00:00 [php] <defunct> root 2973 2971 0 14:13 pts/0 00:00:00 [php] <defunct> root 2974 2971 0 14:13 pts/0 00:00:00 [php] <defunct> root 2975 2971 0 14:13 pts/0 00:00:00 [php] <defunct> root 2976 2971 0 14:13 pts/0 00:00:00 [php] <defunct> root 2978 2912 0 14:13 pts/1 00:00:00 grep php
僵尸进程会用
PHP的pcntl扩展提供了两个回收子进程的方法供我们调用:
int pcntl_wait ( int &$status [, int $options = 0 ] ) int pcntl_waitpid ( int $pid , int &$status [, int $options = 0 ] )
pcntl_wait函数挂起当前进程的执行直到一个子进程退出或接收到一个信号要求中断当前进程或调用一个信号处理函数。 如果一个子进程在调用此函数时已经退出(俗称僵尸进程),此函数立刻返回。子进程使用的所有系统资源将被释放。
关于wait在您系统上工作的详细规范请查看您系统的wait(2)手册。这个函数等同于以-1作为参数pid 的值并且没有options参数来调用pcntl_waitpid() 函数。
代码示例:
<?php $ppid = posix_getpid(); //记录父进程的进程号 for($i = 0; $i < 5; $i++) { $pid = pcntl_fork(); if ($pid == 0) { break; //由于子进程也会执行循环的代码,所以让子进程退出循环 } } if ($ppid == posix_getpid()) { //父进程循环回收收子进程 while(($id = pcntl_wait($status)) > 0) //如果没有子进程退出, pcntl_wait 会一直阻塞 { echo "回收子进程:$id, 子进程退出状态值: $status...\n"; } exit("父进程退出 $id....\n"); //当子进程全部结束 pcntl_wait 返回-1 } else { //子进程退出,会成为僵尸进程 sleep($i); exit($i); }
运行结果:
[root@localhost php]# php pcntl.fork.php 回收子进程:3043, 子进程退出状态值: 0... 回收子进程:3044, 子进程退出状态值: 256... 回收子进程:3045, 子进程退出状态值: 512... 回收子进程:3046, 子进程退出状态值: 768... 回收子进程:3047, 子进程退出状态值: 1024... 父进程退出 -1....
这里只是对PHP多进程编程做了基本的介绍,后面会结合 信号、进程间通信以及守护进程 做更进一步的介绍,欢迎大家关注后续文章。
PHP是世界上最好的语言 That's all :)
相关推荐:
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!