Maison  >  Article  >  développement back-end  >  Une introduction préliminaire à la programmation multi-processus PHP

Une introduction préliminaire à la programmation multi-processus PHP

不言
不言original
2018-07-05 10:37:061237parcourir

Cet article présente principalement l'introduction initiale à la programmation multi-processus PHP. Il a une certaine valeur de référence. Maintenant, je le partage avec tout le monde. Les amis dans le besoin peuvent s'y référer

J'envie Naruto dans le clone de Naruto Shadow. ? Oui, les programmes PHP peuvent activer des clones fantômes ! Si vous souhaitez terminer une tâche mais estimez qu'un processus est trop lent, essayez d'utiliser plusieurs processus. Cet article présentera les besoins de base du multi-processus PHP, comment créer un multi-processus et un contrôle de signal de base. Il ne vous dira pas comment communiquer et partager des informations entre les processus pour le moment.

1. Préparation

Avant de commencer, assurez-vous que vous n'utilisez pas la plateforme M$ Windows (car je n'ai pas Windows). Linux/BSD/Unix ne devrait poser aucun problème. Après avoir confirmé l'environnement de travail, voyons si les modules PHP dont nous avons besoin sont disponibles. Ouvrez le terminal et entrez la commande suivante :

$ php -m

Cette commande vérifie et imprime toutes les extensions PHP actuellement activées pour voir si pcntl et posix sont dans la liste de sortie.

1.1. pcntl

Si pcntl est introuvable, c'est dans 80% des cas que cette extension n'a pas été compilée lors de la compilation. Si vous avez compilé et installé PHP comme moi, vous devez recompiler et installer PHP. N'oubliez pas d'ajouter le paramètre --enable-pcntl lors de la configuration.

$ cd /path/to/php_source_code_dir 
$ ./configure [some other options] --enable-pcntl
$ make && make install

1.2. posix

est généralement installé par défaut, tant que vous n'ajoutez pas --disable-posix lors de la compilation.

2. Connaissances préliminaires

Avant de continuer, vous devez également avoir une petite compréhension du multi-processus Linux. Que se passe-t-il avec le multi-traitement ? C'est légèrement différent du clone de l'ombre dans Naruto. Tout d'abord, Naruto a grandi dès son plus jeune âge, environ 16 ans, en toussant. Un jour, il a activé son clone d'ombre et s'est divisé en cinq. Evidemment, ces clones sont aussi des Naruto de 16 ans et non des bébés qui pleurent à leur naissance et ne comprennent rien (c'est ce qu'on appelle le clonage). Ensuite, voici la différence : les clones sont devenus des personnes indépendantes faisant leurs propres choses, et ils ne savent plus ce que les autres clones et le corps d'origine ont fait (bien sûr, ils n'accumuleront pas d'expérience comme dans le dessin animé) À la personne d'origine) . À moins qu’ils ne communiquent entre eux, seules les choses antérieures à 16 ans constituent leurs souvenirs communs.

Un camarade de classe a dit, patron, tu ne me trompes pas ? Je n'ai pas regardé Naruto ! Ensuite, vous devriez y jeter un œil...

Enfin, les connaissances préparatoires sont terminées, il suffit d'avoir une compréhension générale des processus enfants lancés par le processus principal. Le code du processus enfant est exactement le même que celui du processus principal, et l'autre partie de la même chose est tout exécuté jusqu'au lancement du clone fantôme. Pour plus de détails, veuillez vous référer au cours « Système d'exploitation ».

3. L'art du clone d'ombre

Alors, comment pouvez-vous comprendre le contenu du parchemin sans quelques connaissances de base ? Lorsque j’ai ouvert le parchemin, j’ai vu pour la première fois un mot : fourchette.

3.1. fourchette

Fourchette ? Les fourchettes sont bifurquées, on devient multiple ! C’est à peu près ce que cela signifie. Utilisez cette commande pour créer un processus enfant. Vous devez utiliser la fonction pcntl_fork() ici. (Vous pouvez d'abord jeter un bref coup d'œil à l'introduction de cette fonction dans le manuel PHP.) Créer un script PHP : la fonction

$pid = pcntl_fork(); // 一旦调用成功,事情就变得有些不同了
if ($pid == -1) {    
die('fork failed');
} else if ($pid == 0) {} else {}

pcntl_fork() crée un processus enfant La seule différence entre le processus enfant. et le processus parent est le PID (ID de processus) et le PPID (ID de processus parent) sont différents. Pour afficher le processus dans le terminal, utilisez la commande ps (demandez à l'homme comment utiliser ps : man ps). Lorsque la fonction renvoie -1, cela signifie que le fork a échoué. Essayez d'ajouter la phrase : if avant echo $pid . PHP_EOL;. Exécutez votre script, le résultat peut ressembler à ce qui suit (le résultat montre que le code du processus enfant et du processus parent sont les mêmes) :

67789 # 这个是父进程打印的
0     # 这个是子进程打印的

pcntl_fork()Une fois l'appel de fonction réussi, le processus enfant sera renvoyé dans le PID du processus parent et 0 est renvoyé dans le processus enfant. Par conséquent, utilisons directement la branche if pour contrôler le processus parent et le processus enfant pour faire des choses différentes.

3.2. Attribuer des tâches

Parlons ensuite du clone de l'ombre de Naruto quand il avait 16 ans, et attribuons deux tâches de sortie simples au corps et au clone d'origine : ​​

$parentPid = getmypid(); // 这就是传说中16岁之前的记忆
$pid = pcntl_fork(); // 一旦调用成功,事情就变得有些不同了
if ($pid == -1) {    
die('fork failed');
} else if ($pid == 0) {    
$mypid = getmypid(); // 用getmypid()函数获取当前进程的PID    
echo 'I am child process. My PID is ' . $mypid . ' and my father's PID is ' . $parentPid . PHP_EOL;
} else {    
echo 'Oh my god! I am a father now! My child's PID is ' . $pid . ' and mine is ' . $parentPid . PHP_EOL;}

Le résultat de sortie peut ressembler à ceci :

Oh my god! I am a father now! My child's PID is 68066 and mine is 68065
I am child process. My PID is 68066 and my father's PID is 68065

Je voudrais souligner encore une fois qu'une fois l'appel pcntl_fork() réussi, un programme devient deux programmes : la valeur de la variable $pid obtenue par un programme est 0, c'est un processus enfant ; la valeur de $pid obtenue par un autre programme est supérieure à 0, cette valeur est le PID du processus enfant, qui est le processus parent. Dans l'instruction de branche ci-dessous, différents codes sont exécutés en raison de différentes valeurs $pid. Permettez-moi de le souligner encore une fois : le code du processus enfant est le même que celui du processus parent. Par conséquent, différentes tâches doivent leur être assignées via des instructions de branche.

3.3. 子进程回收

刚刚有man ps么?一般我习惯用ps aux加上grep命令来查找运行着的后台进程。其中有一列STAT,标识了每个进程的运行状态。这里,我们关注状态Z:僵尸(Zombie)。当子进程比父进程先退出,而父进程没对其做任何处理的时候,子进程将会变成僵尸进程。Oops,又跟火影里的影分身不一样了。鸣人的影分身被干死了以后就自动消失了,但是这里的子进程分身死了话还留着一个空壳在,直到父进程回收它。僵尸进程虽然不占什么内存,但是很碍眼,院子里一堆躺着的僵尸怎么都觉得怪怪的。(别忘了它们还占用着PID)

一般来说,在父进程结束之前回收挂掉的子进程就可以了。在pcntl扩展里面有一个pcntl_wait()函数,它会将父进程挂起,直到有一个子进程退出为止。如果有一个子进程变成了僵尸的话,它会立即返回。所有的子进程都要回收,所以多等等也没关系啦!

3.4. 父进程先挂了

如果父进程先挂了怎么办?会发生什么?什么也不会发生,子进程依旧还在运行。但是这个时候,子进程会被交给1号进程,1号进程成为了这些子进程的继父。1号进程会很好地处理这些进程的资源,当它们结束时1号进程会自动回收资源。所以,另一种处理僵尸进程的临时办法是关闭它们的父进程。

4. 信号

一般多进程的事儿讲到上面就完了,可是信号在系统中确实是一个非常重要的东西。信号就是信号灯,点亮一个信号灯,程序就会做出反应。这个你一定用过,比如说在终端下运行某个程序,等了半天也没什么反应,可能你会按 Ctrl+C 来关闭这个程序。实际上,这里就是通过键盘向程序发送了一个中断的信号:SIGINT。有时候进程失去响应了还会执行kill [PID]命令,未加任何其他参数的话,程序会接收到一个SIGTERM信号。程序收到上面两个信号的时候,默认都会结束执行,那么是否有可能改变这种默认行为呢?必须能啊!

4.1. 注册信号

人是活的程序也是活的,只不过程序需要遵循人制定的规则来运行。现在开始给信号重新设定规则,这里用到的函数是pcntl_signal()(继续之前为啥不先查查PHP手册呢?)。下面这段程序将给SIGINT重新定义行为,注意看好:

// 定义一个处理器,接收到SIGINT信号后只输出一行信息
function signalHandler($signal) {    
if ($signal == SIGINT) {        
echo 'signal received' . PHP_EOL;    }}
// 信号注册:当接收到SIGINT信号时,调用signalHandler()函数pcntl_signal(SIGINT, 'signalHandler');while (true) {    sleep(1);    // do something    pcntl_signal_dispatch(); // 接收到信号时,调用注册的signalHandler()}

执行一下,随时按下 Ctrl+C 看看会发生什么事。

4.2. 信号分发

说明一下:pcntl_signal()函数仅仅是注册信号和它的处理方法,真正接收到信号并调用其处理方法的是pcntl_signal_dispatch()函数。试试把// do something替换成下面这段代码:

for ($i = 0; $i < 1000000; $i++) {    
echo $i . PHP_EOL;    
usleep(100000);}

在终端下执行这个脚本,当它不停输出数字的时候尝试按下 Ctrl+C 。看看程序有什么响应?嗯……什么都没有,除了屏幕可能多了个^C以外,程序一直在不停地输出数字。因为程序一直没有执行到pcntl_signal_dispatch(),所以就并没有调用signalHandler(),所以就没有输出signal received

4.3. 版本问题

如果认真看了PHP文档,会发现pcntl_signal_dispatch()这个函数是PHP 5.3以上才支持的,如果你的PHP版本大于5.3,建议使用这个方法调用信号处理器。5.3以下的版本需要在注册信号之前加一句:declare(ticks = 1);表示每执行一条低级指令,就检查一次信号,如果检测到注册的信号,就调用其信号处理器。想想就挺不爽的,干嘛一直都检查?还是在我们指定的地方检查一下就好。

4.4. 感受僵尸进程

现在我们回到子进程回收的问题上(差点忘了= =")。当你的一个子进程挂了(或者说是结束了),但是父进程还在运行中并且可能很长一段时间不会退出。一个僵尸进程从此站起来了!这时,保护伞公司(内核)发现它的地盘里出现了一个僵尸,这个僵尸是谁儿子呢?看一下PPID就知道了。然后,内核给PPID这个进程(也就是僵尸进程的父进程)发送一个信号:SIGCHLD。然后,你知道怎么在父进程中回收这个子进程了么?提示一下,用pcntl_wait()函数。

4.5. Envoyer le signal

J'espère que vous avez soigneusement exécuté la commande kill tout à l'heure. Cela envoie en fait un signal au processus. Vous pouvez également appeler la fonction posix_kill() en PHP pour obtenir le même effet. Avec lui, vous pouvez contrôler l'exécution d'autres processus enfants dans le processus parent. Par exemple, pour fermer tous les processus enfants avant la fin du processus parent, enregistrez les PID de tous les processus enfants dans le processus parent à fork et envoyez des signaux de fin aux processus enfants dans l'ordre avant la fin du processus parent.

5. Pratique

Le multi-processus de PHP est assez similaire à celui du C. Une fois que vous l'aurez compris, ce sera similaire si vous l'écrivez dans d'autres langages. Si vous avez le temps, essayez d'écrire un petit programme et expérimentez-le personnellement :

  1. Naruto, 16 ans, a envoyé des clones fantômes et s'est divisé en 5 clones

  2. Chaque clone survit de manière aléatoire pendant 10 à 30 secondes, produisant quelque chose toutes les secondes

  3. Assurez-vous que le clone d'origine peut sentir la fin du clone, puis activez un autre clone. Assurez-vous que. il y a au plus 5 clones

  4. Ne pas utiliser nohup, afin que le clone d'origine puisse toujours s'exécuter après la fermeture du terminal

  5. Augmentez le nombre de clones (5) Écrivez-le dans un fichier de configuration Lors de l'envoi d'un signal au corps d'origine (pensez à utiliser SIGUSR1 ou SIGUSR2), le corps d'origine lit le fichier de configuration et met à jour le nombre maximum de clones autorisés

  6. S'il y a trop de clones, fermez-en quelques-uns ; s'il y en a trop peu, divisez-en quelques autres

Conseils :

  1. Utiliser while la garantie de boucle Le processus est en cours d'exécution, veillez sleep à éviter une utilisation à 100 % du processeur

  2. Lorsque le terminal exécutant le processus est fermé, le le programme recevra un signal SIGHUP

  3. Vous pouvez utiliser la fonction parse_ini_file() pour analyser le fichier de configuration INI

Ce qui précède est l'intégralité du contenu de cet article. J'espère qu'il sera utile à l'apprentissage de tout le monde. Pour plus de contenu connexe, veuillez faire attention au site Web PHP chinois !

Recommandations associées :

Compréhension du polymorphisme PHP

Introduction à la programmation de fichiers PHP

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:
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