Heim  >  Artikel  >  Backend-Entwicklung  >  PHP implementiert die Systemprogrammierung: Einführung in die Multiprozessprogrammierung sowie in verwaiste Prozesse und Zombie-Prozesse

PHP implementiert die Systemprogrammierung: Einführung in die Multiprozessprogrammierung sowie in verwaiste Prozesse und Zombie-Prozesse

不言
不言Original
2018-04-13 10:39:411883Durchsuche

Der Inhalt dieses Artikels besteht darin, Ihnen die Einführung von Multiprozessprogrammierung und verwaisten Prozessen sowie Zombie-Prozessen in der PHP-Systemprogrammierung zu vermitteln. Er hat einen gewissen Referenzwert

Multiprozessprogrammierung ist auch ein wichtiger Aspekt der Systemprogrammierung, aber PHP-Programmierer müssen sich normalerweise nicht um Multiprozessprobleme kümmern, da der Webserver oder PHP-FPM die Prozessprobleme bereits für uns verwaltet hat, aber wenn Wir möchten PHP verwenden. Bei der Entwicklung von CLI-Programmen ist die Multiprozessprogrammierung eine unverzichtbare Basistechnologie.

Die Prozesssteuerungsmethode in PHP verwendet hauptsächlich die PCNTL-Erweiterung (Process Control). Daher müssen Sie vor der Durchführung der Multiprozessprogrammierung zunächst sicherstellen, dass auf Ihrem PHP die neueste PCNTL installiert ist. Erweiterungen: Sie können den Befehl

php -m eingeben, um die aktuell installierten Erweiterungen anzuzeigen:



Diese Erweiterung stellt uns eine Reihe von Methoden für Prozessoperationen zur Verfügung:

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_forkErzeugt einen Zweig (Kind) an der aktuellen Position des aktuellen Prozessablaufs). Anmerkung: Fork erstellt einen untergeordneten Prozess, einen übergeordneten Prozess und einen untergeordneten Prozess Sie beginnen alle an der Gabelposition und setzen sich nach unten fort. Der Unterschied besteht darin, dass der erhaltene Gabelrückgabewert während der Ausführung des übergeordneten Prozesses die Nummer des untergeordneten Prozesses ist und der untergeordnete Prozess 0 erhält.

Der gegabelte untergeordnete Prozess ist fast eine vollständige Kopie des übergeordneten Prozesses. Der übergeordnete und untergeordnete Prozess teilen sich Codesegmente, obwohl das Datensegment, der Heap und der Stapel vorhanden sind Der übergeordnete und untergeordnete Prozess sind voneinander unabhängig, aber zu Beginn kopiert der untergeordnete Prozess die Daten des übergeordneten Prozesses vollständig, spätere Änderungen wirken sich jedoch nicht gegenseitig aus.


int pcntl_fork ( void )


Erstellen Sie eine Codedemonstration mit 5 Unterprozessen:

<?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);
}
Ergebnis ausführen:



[root@localhost process]# php process.php 
第 1 个子进程退出...1503322773
第 2 个子进程退出...1503322774
第 3 个子进程退出...1503322775
第 4 个子进程退出...1503322776
第 5 个子进程退出...1503322777
父进程退出...1503322778

Es ist wichtig, die Funktion pcntl_fork zu verstehen:
"fork erstellt einen untergeordneten Prozess, Der übergeordnete Prozess und der untergeordnete Prozess werden weiterhin von der Fork-Position aus ausgeführt. Der Unterschied besteht darin, dass während der Ausführung des übergeordneten Prozesses der Fork-Rückgabewert die Nummer des untergeordneten Prozesses ist und der untergeordnete Prozess 0 erhält



Ändern Sie den obigen Code leicht, um zu verhindern, dass der Prozess beendet wird, und verwenden Sie dann den Befehl ps, um den Systemstatus zu überprüfen:

<?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);       //执行死循环不退出
}

Geben Sie nach dem Ausführen ps -ef grep php | ein, um den Systemprozess anzuzeigen


[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

Sie können 6 PHP-Process.php-Prozesse sehen, die zweite Spalte ist der Prozess Die dritte Spalte ist die Prozessnummer des übergeordneten Prozesses. Sie können sehen, dass die übergeordneten Prozessnummern der nächsten fünf Prozesse alle Prozessnummern des ersten Prozesses sind.


Der obige Code führt sowohl für den untergeordneten Prozess als auch für den übergeordneten Prozess denselben Code aus Verschiedene Dinge tun? Der einfachste Weg besteht darin, zu beurteilen, ob der untergeordnete Prozess den Code des untergeordneten Prozesses ausführt und der übergeordnete Prozess den Code des übergeordneten Prozesses ausführt:

<?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 ...

Tatsächlich führen die übergeordneten und untergeordneten Prozesse des obigen Programms immer noch denselben Code aus, aber die if-Verzweigungen Die eingegebenen Werte sind unterschiedlich und pcntl_exec kann den Einfluss des untergeordneten Prozesses vollständig vom übergeordneten Prozess trennen, um neue Prozeduren zu implementieren.

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(&#39;/bin/ls&#39;);  //执行 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


僵尸进程会用 21eba13340b1d3a525d7480ca5bbd370(死者,死人) 来标识,除非我们结束父进程,否则这些僵尸进程会一直存在,也无法用kill命令来杀死。

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

相关推荐:

PHP实现系统编程之网络Socket及IO多路复用

Das obige ist der detaillierte Inhalt vonPHP implementiert die Systemprogrammierung: Einführung in die Multiprozessprogrammierung sowie in verwaiste Prozesse und Zombie-Prozesse. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn