Maison  >  Article  >  développement back-end  >  Explication détaillée de l'implémentation du démon démon dans PHP7

Explication détaillée de l'implémentation du démon démon dans PHP7

little bottle
little bottleavant
2019-04-25 14:11:473244parcourir

Cet article parle principalement de l'utilisation de PHP7 pour implémenter le processus démon. Il a une certaine valeur de référence. Les amis intéressés peuvent en apprendre davantage.

Dans un système d'exploitation informatique multitâche, un processus démon est un programme informatique qui s'exécute en arrière-plan. De tels programmes seront initialisés en tant que processus. Les noms des programmes démons se terminent généralement par la lettre « d » : par exemple, syslogd fait référence au démon qui gère les journaux système.

Le programme démon est un programme serveur qui est toujours en cours d'exécution, également appelé processus démon. Habituellement, il s'exécute en arrière-plan du système. Il n'y a pas de terminal de contrôle et il n'interagit pas avec le premier plan. Le programme démon est généralement utilisé comme service système. Un démon est un processus de longue durée qui s'exécute généralement après le démarrage du système et se termine lorsque le système s'arrête. De manière générale, le programme Daemon s'exécute en arrière-plan car il ne dispose pas de terminal de contrôle et ne peut pas interagir avec les utilisateurs au premier plan. Les programmes démons sont généralement utilisés comme programmes de service, attendant que les programmes clients communiquent avec eux. Nous appelons également le programme démon en cours d'exécution un processus démon.

Habituellement, le processus démon n'a aucun processus parent existant (c'est-à-dire PPID=1) et se trouve directement sous init dans la hiérarchie des processus du système UNIX. Un processus démon se transforme généralement en démon par la méthode suivante : Exécutez fork sur un processus enfant, puis terminez immédiatement son processus parent afin que le processus enfant puisse être utilisé dans init Exécuter sous . Cette méthode est souvent appelée « bombardement ».

Le système démarre généralement le processus démon au démarrage. Les démons permettent de répondre aux requêtes réseau, à l'activité matérielle ou à d'autres requêtes provenant d'autres applications via certaines tâches. Les démons peuvent également configurer le matériel (comme devfsd sur certains systèmes Linux), exécuter des tâches planifiées (telles que cron) et exécuter d'autres tâches. Chaque processus a un processus parent. Lorsque le processus enfant se termine, le processus parent peut obtenir l'état de sortie du processus enfant.

Un processus démon est simplement un processus qui peut s'exécuter en arrière-plan sans le terminal. Il s'agit d'un processus très courant sous Linux, par exemple après qu'un service tel qu'Apache ou MySQL soit exécuté. démarré, il résidera en mémoire en tant que processus démon. Les démons sont des applications qui s'exécutent en arrière-plan et ne sont pas directement exploitées par l'utilisateur. Des exemples de démons sont Cron et MySQL. L'utilisation du démon PHP est très simple et nécessite des paramètres de compilation avec PHP 4.1 ou supérieur : --enable-pcntl

Supposons qu'il y ait une tâche fastidieuse qui doit être exécutée en arrière-plan : importer les 20 millions d'utilisateurs de la table utilisateur de tout MySQL dans Redis pour préchauffer le cache, alors cette tâche ne se terminera probablement pas avant un moment. le temps, vous devez écrire un script php à exécuter dans le système en tant que démon et le lancer automatiquement une fois terminé.

Sous Linux, il existe trois façons d'effectuer un script en arrière-plan :

1. Ajoutez une esperluette après la commande <.>

Par exemple, php task.php &. L'inconvénient de cette méthode est que si le terminal est fermé, qu'il soit fermé normalement ou anormalement, le processus php sera fermé lorsque le terminal est fermé. Deuxièmement, s'il y a du texte de sortie tel que echo ou print_r dans le code, il sera affiché dans la fenêtre de terminal actuelle.

2. Utilisez la commande nohup

telle que nohup php task.php & . le code Ou le texte généré par print_r sera affiché dans le fichier nohup.out dans le même répertoire que le code PHP Si vous fermez le terminal par des moyens normaux tels que la commande exit ou le bouton de fermeture, le processus ne sera pas fermé. et continuera à s'exécuter en arrière-plan. Cependant, si le terminal rencontre une sortie ou un arrêt anormal, le processus php se terminera également immédiatement. En substance, ce n'est pas une solution démon stable et fiable.

3. En étendant et pcntl pour mettre en œuvre posix

, des choses qui doivent être notées. en programmation sont :

  • Laisser le processus principal quitter le terminal via le pcntl_fork() secondaire et posix_setsid
  • Ignorer ou gérer le pcntl_signal()signalSIGHUP via
  • Les programmes multi-processus doivent passer deux fois pcntl_fork() ou pcntl_signal() ignorer le signal SIGCHLD pour empêcher le processus enfant de devenir un processus zombie
  • passer umask() Définir le masque d'autorisation de fichier pour empêcher l'influence des autorisations héritées des autorisations de fichier
  • Rediriger le processus en cours STDIN/STDOUT/STDERR vers /dev/null ou d'autres flux

Le démon a les caractéristiques suivantes :

  • Aucun terminal
  • Exécution en arrière-plan
  • Le pid du processus parent est 1

Si vous souhaitez afficher le démon en cours d'exécution processus, vous pouvez passer ps -ax ou ps -ef View, où -x signifie que les processus sans contrôle des terminaux seront répertoriés.

Appel système fork

L'appel système fork est utilisé pour copier un processus qui est presque identique au processus parent L'enfant nouvellement généré. Le processus est différent. Le problème est qu'il a un pid et un espace mémoire différents de ceux du processus parent. Selon la logique du code, les processus parent et enfant peuvent effectuer le même travail, ou ils peuvent être différents. Le processus enfant hérite des ressources telles que les descripteurs de fichiers du processus parent.

L'extension pcntl en PHP implémente la fonction pcntl_fork(), qui est utilisée pour créer un nouveau processus en PHP.

Appel système setsid

L'appel système setsid est utilisé pour créer une nouvelle session et définir l'identifiant du groupe de processus. Il y a plusieurs concepts ici : 会话, 进程组.

Sous Linux, la connexion utilisateur génère une session. Une session contient un ou plusieurs groupes de processus, et un groupe de processus contient plusieurs processus. Chaque groupe de processus a un chef de groupe (Session Leader), et son pid est l'identifiant de groupe du groupe de processus. Une fois que le responsable du processus ouvre un terminal, ce terminal est appelé terminal de contrôle. Dès qu'une exception se produit dans le terminal de contrôle (déconnexion, erreur matérielle, etc.), un signal sera envoyé au chef du groupe de processus.

Les programmes exécutés en arrière-plan (tels que les instructions d'exécution se terminant par & dans le shell) seront également supprimés après la fermeture du terminal, c'est-à-dire le SIGHUP, et le comportement par défaut du signal SIGHUP pour un processus est de quitter le processus.

Appel setsid Après l'appel système, le processus en cours créera un nouveau groupe de processus. Si le terminal n'est pas ouvert dans le processus en cours, alors. celui-ci Il n'y aura pas de terminal de contrôle dans le groupe de processus, et il n'y aura aucun problème pour tuer le processus en fermant le terminal.

L'extension en PHP implémente la fonction posix, qui est utilisée pour définir un nouveau groupe de processus en PHP. posix_setsid()

Le rôle du fork secondaire

Tout d'abord, l'appel système ne peut pas être appelé par le chef du groupe de processus et reviendra -1. setsid

L'exemple de code pour l'opération de fork secondaire est le suivant :

&lt;span style=&quot;font-size: 16px;&quot;&gt;$pid1 = pcntl_fork();

if ($pid1 &gt; 0) {&lt;/span&gt;&lt;br/&gt;&lt;span style=&quot;font-size: 16px;&quot;&gt;    // 父进程会得到子进程号,所以这里是父进程执行的逻辑
    exit(&amp;#39;parent process. 1&amp;#39;.&quot;\n&quot;);
} else if ($pid1 &lt; 0) {
    exit(&quot;Failed to fork 1\n&quot;);
}

if (-1 == posix_setsid()) {
    exit(&quot;Failed to setsid\n&quot;);
}

$pid2 = pcntl_fork();

if ($pid2 &gt; 0) {
    exit(&amp;#39;parent process. 2&amp;#39;.&quot;\n&quot;);
} else if ($pid2 &lt; 0) {
    exit(&quot;Failed to fork 2\n&quot;);
}&lt;/span&gt;

La fonction pcntl_fork() crée un processus enfant , ce Le processus enfant ne diffère de son processus parent que par le PID (ID de processus) et le PPID (ID de processus parent).

Valeur de retour

En cas de succès, le PID du processus enfant généré est renvoyé dans le thread d'exécution du processus parent et est exécuté dans le processus enfant 0 est renvoyé dans le thread. Lorsque échoue, -1 est renvoyé dans le contexte du processus parent. Le processus enfant ne sera pas créé et une erreur PHP. sera relevé.

假定我们在终端中执行应用程序,进程为 a,第一次 fork 会生成子进程 b,如果 fork 成功,父进程 a 退出。b 作为孤儿进程,被 init 进程托管。

此时,进程 b 处于进程组 a 中,进程 b 调用 posix_setsid 要求生成新的进程组,调用成功后当前进程组变为 b。


php fork2.php 
parent process. 1
parent process. 2

此时进程 b 事实上已经脱离任何的控制终端,例程:


cli_set_process_title(&amp;#39;process_a&amp;#39;);

$pidA = pcntl_fork();

if ($pidA &gt; 0) {
    exit(0);
} else if ($pidA &lt; 0) {
    exit(1);
}

cli_set_process_title(&amp;#39;process_b&amp;#39;);

if (-1 === posix_setsid()) {
    exit(2);
}

while(true) {
    sleep(1);
}

执行程序之后:  


$ php cli-title.php 
$ ps ax | grep -v grep | grep -E &amp;#39;process_|PID&amp;#39;
  PID TTY      STAT   TIME COMMAND
15725 ?        Ss     0:00 process_b

重新打开一个shell窗口,效果一样,都在呢

从 ps 的结果来看,process_b 的 TTY 已经变成了 ,即没有对应的控制终端。

代码走到这里,似乎已经完成了功能,关闭终端之后 process_b 也没有被杀死,但是为什么还要进行第二次 fork 操作呢?

StackOverflow 上的一个回答写的很好:

The second fork(2) is there to ensure that the new process is not a session leader, so it won’t be able to (accidentally) allocate a controlling terminal, since daemons are not supposed to ever have a controlling terminal.

这是为了防止实际的工作的进程主动关联或者意外关联控制终端,再次 fork 之后生成的新进程由于不是进程组组长,是不能申请关联控制终端的。

综上,二次 fork 与 setsid 的作用是生成新的进程组,防止工作进程关联控制终端。 

写一个demo测试下


&lt;?php
// 第一次fork系统调用
$pid_A = pcntl_fork();

// 父进程 和 子进程 都会执行下面代码
if ($pid_A &lt; 0) {
    // 错误处理: 创建子进程失败时返回-1.
    exit(&amp;#39;A fork error &amp;#39;);
} else if ($pid_A &gt; 0) {
     // 父进程会得到子进程号,所以这里是父进程执行的逻辑
    exit(&quot;A parent process exit \n&quot;);
}

// B 作为孤儿进程,被 init 进程托管,此时,进程 B 处于进程组 A 中

// 子进程得到的$pid为0, 所以以下是子进程执行的逻辑,受控制终端的影响,控制终端关闭则这里也会退出

// [子进程] 控制终端未关闭前,将当前子进程提升会会话组组长,及进程组的leader
// 进程 B 调用 posix_setsid 要求生成新的进程组,调用成功后当前进程组变为 B
if (-1 == posix_setsid()) {
    exit(&quot;Failed to setsid\n&quot;);
}

// 此时进程 B 已经脱离任何的控制终端

// [子进程]  这时候在【进程组B】中,重新fork系统调用(二次fork)
$pid_B = pcntl_fork();
if ($pid_B &lt; 0) {
    exit(&amp;#39;B fork error &amp;#39;);
} else if ($pid_B &gt; 0) {
    exit(&quot;B parent process exit \n&quot;);
}

// [新子进程] 这里是新生成的进程组,不受控制终端的影响,写写自己的业务逻辑代码
for ($i = 1; $i &lt;= 100; $i++) {
    sleep(1);
    file_put_contents(&amp;#39;daemon.log&amp;#39;,$i . &quot;--&quot; . date(&quot;Y-m-d H:i:s&quot;, time()) . &quot;\n&quot;,FILE_APPEND);
}

Window 下跑回直接抛出异常


php runtime\daemon.php
PHP Fatal error:  Uncaught Error: Call to undefined function pcntl_fork() in D:\phpStudy\PHPTutorial\WWW\notes\runtime\daemon.php:13
Stack trace:
#0 {main}
  thrown in D:\phpStudy\PHPTutorial\WWW\notes\runtime\daemon.php on line 13

Linux 下执行,输出结果


&lt;span style=&quot;font-size: 16px;&quot;&gt;php daemon.php&lt;/span&gt;&lt;br/&gt;&lt;span style=&quot;font-size: 16px;&quot;&gt;...
97--2018-09-07 03:50:09
98--2018-09-07 03:50:10
99--2018-09-07 03:50:11
100--2018-09-07 03:50:12&lt;/span&gt;

所以,现在即使关闭了终端,改脚本任然在后台守护进程运行

相关教程: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:
Cet article est reproduit dans:. en cas de violation, veuillez contacter admin@php.cn Supprimer