ホームページ バックエンド開発 PHPチュートリアル PHP によるシステム プログラミングの実装: マルチプロセス プログラミングと孤立プロセスとゾンビ プロセスの概要

PHP によるシステム プログラミングの実装: マルチプロセス プログラミングと孤立プロセスとゾンビ プロセスの概要

Apr 13, 2018 am 10:39 AM
php プログラミング プロセス

この記事の内容は、PHP システムプログラミングにおけるマルチプロセスプログラミングと孤立プロセスとゾンビプロセスの概要を共有することです。必要な友人は参考にしてください。

マルチプロセスプログラミングは。これもシステム プログラミングの一部であり、重要な側面ですが、Web サーバーまたは PHP-FPM がすでにプロセスの問題を管理しているため、通常、PHP プログラマーはマルチプロセスの問題を気にする必要はありませんが、PHP を使用して開発する場合は、 CLIプログラム、マルチプロセスプログラミングは欠かせない基礎技術です。

PHP のプロセス制御方法は主に PCNTL (プロセス制御) 拡張機能を使用するため、マルチプロセス プログラミングを実行する前に、まず PHP に最新の PCNTL 拡張機能がインストールされていることを確認する必要があります。 - 現在インストールされている拡張機能を表示するための m コマンド:



この拡張機能は、プロセス操作のための一連のメソッドを提供します:

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 現在のプロセスの現在位置にブランチ (子プロセス) を作成します。注釈: フォークにより子プロセス、親プロセス、および子プロセスが作成されます。 これらはすべてフォーク位置から開始され、下方向に継続します。違いは、親プロセスの実行中に取得されるフォークの戻り値が子プロセスの番号であり、子プロセスは 0 を取得することです。

フォークされた子プロセスは親プロセスのほぼ完全なコピーですが、親プロセスと子プロセスのデータセグメント、ヒープ、スタックは互いに独立しています。最初に、子プロセスは完全に親プロセスのデータをコピーしますが、その後の変更は相互に影響しません。


int pcntl_fork ( void )
ログイン後にコピー


5 つのサブプロセスを作成する コードのデモ:結果:

<?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);
}
ログイン後にコピー


pcntl_fork 関数について理解しておくべき重要な点:

fork は子プロセスを作成します。親プロセスと子プロセスはどちらもフォーク位置から実行を継続します。違いは、親プロセスの実行中にフォークが返されることです。 value は子プロセス番号であり、子プロセスは 0" を取得します


プロセスが終了しないように上記のコードを少し変更し、ps コマンドを使用してシステムのステータスを確認します。


[root@localhost process]# php process.php 
第 1 个子进程退出...1503322773
第 2 个子进程退出...1503322774
第 3 个子进程退出...1503322775
第 4 个子进程退出...1503322776
第 5 个子进程退出...1503322777
父进程退出...1503322778
ログイン後にコピー


実行後、 ps -ef | grep php と入力してシス​​テムプロセスを表示します

<?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);       //执行死循环不退出
}
ログイン後にコピー


2番目の列に6つのphp process.phpプロセスが表示されます。はプロセス番号、3列目はプロセスの親プロセス番号です。次の5つのプロセスの親プロセス番号はすべて最初のプロセスのプロセス番号であることがわかります。


上記のコードは、子プロセスと親プロセスの両方で同じコードを実行しますが、子プロセスと親プロセスが異なることを行うようにする方法はありますか?プロセスは子プロセスを実行します 親プロセスのコードは親プロセスのコードを実行します:


[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
ログイン後にコピー

<?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);
        }
}
ログイン後にコピー

実際、上記のプログラムの親プロセスと子プロセスは同じコードを実行します。 , ただし、入力される if 分岐が異なり、pcntl_exec は子プロセスを親プロセスの影響から完全に逃れて新しいプログラムを実行できます。

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
ログイン後にコピー


僵尸进程会用 (死者,死人) 来标识,除非我们结束父进程,否则这些僵尸进程会一直存在,也无法用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多路复用

以上がPHP によるシステム プログラミングの実装: マルチプロセス プログラミングと孤立プロセスとゾンビ プロセスの概要の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。

ホットAIツール

Undresser.AI Undress

Undresser.AI Undress

リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover

AI Clothes Remover

写真から衣服を削除するオンライン AI ツール。

Undress AI Tool

Undress AI Tool

脱衣画像を無料で

Clothoff.io

Clothoff.io

AI衣類リムーバー

AI Hentai Generator

AI Hentai Generator

AIヘンタイを無料で生成します。

ホットツール

メモ帳++7.3.1

メモ帳++7.3.1

使いやすく無料のコードエディター

SublimeText3 中国語版

SublimeText3 中国語版

中国語版、とても使いやすい

ゼンドスタジオ 13.0.1

ゼンドスタジオ 13.0.1

強力な PHP 統合開発環境

ドリームウィーバー CS6

ドリームウィーバー CS6

ビジュアル Web 開発ツール

SublimeText3 Mac版

SublimeText3 Mac版

神レベルのコード編集ソフト(SublimeText3)

Ubuntu および Debian 用の PHP 8.4 インストールおよびアップグレード ガイド Ubuntu および Debian 用の PHP 8.4 インストールおよびアップグレード ガイド Dec 24, 2024 pm 04:42 PM

PHP 8.4 では、いくつかの新機能、セキュリティの改善、パフォーマンスの改善が行われ、かなりの量の機能の非推奨と削除が行われています。 このガイドでは、Ubuntu、Debian、またはその派生版に PHP 8.4 をインストールする方法、または PHP 8.4 にアップグレードする方法について説明します。

PHP 開発用に Visual Studio Code (VS Code) をセットアップする方法 PHP 開発用に Visual Studio Code (VS Code) をセットアップする方法 Dec 20, 2024 am 11:31 AM

Visual Studio Code (VS Code とも呼ばれる) は、すべての主要なオペレーティング システムで利用できる無料のソース コード エディター (統合開発環境 (IDE)) です。 多くのプログラミング言語の拡張機能の大規模なコレクションを備えた VS Code は、

PHPでHTML/XMLを解析および処理するにはどうすればよいですか? PHPでHTML/XMLを解析および処理するにはどうすればよいですか? Feb 07, 2025 am 11:57 AM

このチュートリアルでは、PHPを使用してXMLドキュメントを効率的に処理する方法を示しています。 XML(拡張可能なマークアップ言語)は、人間の読みやすさとマシン解析の両方に合わせて設計された多用途のテキストベースのマークアップ言語です。一般的にデータストレージに使用されます

母音を文字列にカウントするPHPプログラム 母音を文字列にカウントするPHPプログラム Feb 07, 2025 pm 12:12 PM

文字列は、文字、数字、シンボルを含む一連の文字です。このチュートリアルでは、さまざまな方法を使用してPHPの特定の文字列内の母音の数を計算する方法を学びます。英語の母音は、a、e、i、o、u、そしてそれらは大文字または小文字である可能性があります。 母音とは何ですか? 母音は、特定の発音を表すアルファベットのある文字です。大文字と小文字など、英語には5つの母音があります。 a、e、i、o、u 例1 入力:string = "tutorialspoint" 出力:6 説明する 文字列「TutorialSpoint」の母音は、u、o、i、a、o、iです。合計で6元があります

コーディングの鍵: 初心者のための Python の力を解き放つ コーディングの鍵: 初心者のための Python の力を解き放つ Oct 11, 2024 pm 12:17 PM

Python は、学習の容易さと強力な機能により、初心者にとって理想的なプログラミング入門言語です。その基本は次のとおりです。 変数: データ (数値、文字列、リストなど) を保存するために使用されます。データ型: 変数内のデータの型 (整数、浮動小数点など) を定義します。演算子: 数学的な演算と比較に使用されます。制御フロー: コード実行のフロー (条件文、ループ) を制御します。

Python による問題解決: 初心者プログラマーとして強力なソリューションをアンロックする Python による問題解決: 初心者プログラマーとして強力なソリューションをアンロックする Oct 11, 2024 pm 08:58 PM

Python は、問題解決の初心者に力を与えます。ユーザーフレンドリーな構文、広範なライブラリ、変数、条件文、ループによる効率的なコード開発などの機能を備えています。データの管理からプログラム フローの制御、反復的なタスクの実行まで、Python が提供します

今まで知らなかったことを後悔している 7 つの PHP 関数 今まで知らなかったことを後悔している 7 つの PHP 関数 Nov 13, 2024 am 09:42 AM

あなたが経験豊富な PHP 開発者であれば、すでにそこにいて、すでにそれを行っていると感じているかもしれません。あなたは、運用を達成するために、かなりの数のアプリケーションを開発し、数百万行のコードをデバッグし、大量のスクリプトを微調整してきました。

C の謎を解く: 新人プログラマーのための明確でシンプルな道 C の謎を解く: 新人プログラマーのための明確でシンプルな道 Oct 11, 2024 pm 10:47 PM

C は、初心者がシステム プログラミングを学習するのに最適な選択肢です。ヘッダー ファイル、関数、メイン関数のコンポーネントが含まれています。 「HelloWorld」を印刷できる単純な C プログラムには、標準入出力関数宣言を含むヘッダー ファイルが必要で、main 関数で printf 関数を使用して印刷します。 C プログラムは、GCC コンパイラーを使用してコンパイルして実行できます。基本をマスターしたら、データ型、関数、配列、ファイル処理などのトピックに進み、熟練した C プログラマーになることができます。

See all articles