PHPのプロセス間通信について詳しく解説

小云云
リリース: 2023-03-21 12:32:01
オリジナル
5207 人が閲覧しました

プロセスは、特定のデータ収集に対して独立した機能を持つプログラムの実行アクティビティです。つまり、システムが複数の CPU をスケジュールするときのプログラムの基本単位です。プロセスはほとんどの言語にとって馴染みのない概念ではありませんが、「世界最高の言語」である PHP は確かに例外です。

環境

phpでの処理は拡張機能という形で完結します。これらの拡張機能を使用すると、プロセス内の一連のアクションを簡単に完了できます。

  • pcntl拡張子: メインプロセスの拡張子であり、待機中の処理で完了プロセスが作成されます。

  • posix 拡張機能: プロセス ID の取得、プロセスの強制終了など、POSIX 互換マシン用の完全な共通 API。

  • sysvmsg 拡張機能: System v モードでのプロセス間通信用のメッセージ キューを実装します。

  • sysvsem 拡張機能: システム v セマフォを実装します。

  • sysvshm 拡張機能: System v モードで共有メモリを実装します。

  • sockets 拡張: ソケット通信を実装します。

これらの拡張機能は Linux/Mac でのみ使用でき、Windows ではサポートされていません。最後に、PHP バージョンは 5.5 以降であることをお勧めします。

簡単な例

シンプルな PHP マルチプロセスの例 この例には、1 つの子プロセスと 1 つの親プロセスがあります。子プロセスは 5 回出力してプログラムを終了します。

$parentPid = posix_getpid();
echo "parent progress pid:{$parentPid}\n";
$childList = array();
$pid = pcntl_fork();
if ( $pid == -1) {
    // 创建失败
    exit("fork progress error!\n");
} else if ($pid == 0) {
    // 子进程执行程序
    $pid = posix_getpid();
    $repeatNum = 5;
    for ( $i = 1; $i <= $repeatNum; $i++) {
        echo "({$pid})child progress is running! {$i} \n";
        $rand = rand(1,3);
        sleep($rand);
    }
    exit("({$pid})child progress end!\n");
} else {
    // 父进程执行程序
    $childList[$pid] = 1;
}
// 等待子进程结束
pcntl_wait($status);
echo "({$parentPid})main progress end!";
ログイン後にコピー

完璧、ついに子プロセスと親プロセスが作成されました。もう終わりですか?いいえ、各プロセスは互いに独立しており、交差する部分はなく、使用範囲は厳しく制限されています。進捗間通信を使ってみましょう。

4. プロセス間通信 (IPC)

通常、Linux のプロセス通信メソッドには、メッセージ キュー、セマフォ、共有メモリ、シグナル、パイプ、ソケットが含まれます。

1. メッセージ キュー

メッセージ キューはメモリに保存されるキューです。次のコードは、3 つのプロデューサー サブプロセスと 2 つのコンシューマー サブプロセスを作成します。これら 5 つのプロセスはメッセージ キューを通じて通信します。

$parentPid = posix_getpid();
echo "parent progress pid:{$parentPid}\n";$childList = array();
// 创建消息队列,以及定义消息类型(类似于数据库中的库)
$id = ftok(__FILE__,&#39;m&#39;);
$msgQueue = msg_get_queue($id);
const MSG_TYPE = 1;
// 生产者
function producer(){
    global $msgQueue;
    $pid = posix_getpid();
    $repeatNum = 5;
    for ( $i = 1; $i <= $repeatNum; $i++) {
        $str = "({$pid})progress create! {$i}";
        msg_send($msgQueue,MSG_TYPE,$str);
        $rand = rand(1,3);
        sleep($rand);
    }
}
// 消费者
function consumer(){
    global $msgQueue;
    $pid = posix_getpid();
    $repeatNum = 6;
    for ( $i = 1; $i <= $repeatNum; $i++) {
        $rel = msg_receive($msgQueue,MSG_TYPE,$msgType,1024,$message);
        echo "{$message} | consumer({$pid}) destroy \n";
        $rand = rand(1,3);
        sleep($rand);
    }
}
function createProgress($callback){
    $pid = pcntl_fork();
    if ( $pid == -1) {
        // 创建失败
        exit("fork progress error!\n");
    } else if ($pid == 0) {
        // 子进程执行程序
        $pid = posix_getpid();
        $callback();
        exit("({$pid})child progress end!\n");
    }else{
        // 父进程执行程序
        return $pid;
    }
}
// 3个写进程
for ($i = 0; $i < 3; $i ++ ) {
    $pid = createProgress(&#39;producer&#39;);
    $childList[$pid] = 1;
    echo "create producer child progress: {$pid} \n";
}
// 2个写进程
for ($i = 0; $i < 2; $i ++ ) {
    $pid = createProgress(&#39;consumer&#39;);
    $childList[$pid] = 1;
    echo "create consumer child progress: {$pid} \n";
}
// 等待所有子进程结束
while(!empty($childList)){
    $childPid = pcntl_wait($status);
    if ($childPid > 0){
        unset($childList[$childPid]);
    }
}
echo "({$parentPid})main progress end!\n";
ログイン後にコピー

メッセージ キュー内のデータにアクセスできるプロセスは 1 つだけであるため、追加のロックやセマフォは必要ありません。

2. セマフォと共有メモリ

セマフォ: システムによって提供されるアトミック操作、セマフォであり、同時に操作できるのはユーザーのプロセスだけです。プロセスがセマフォを取得した場合、プロセスはセマフォを解放する必要があります。

共有メモリ: システムによってメモリ内に開かれる共通のメモリ領域であり、複数のプロセスが同時にアクセスでき、データの一貫性を確保するために必要なメモリ領域です。ロックまたはセマフォになります。

以下では、メモリ内の同じ値を変更する複数のプロセスを作成します。

$parentPid = posix_getpid();
echo "parent progress pid:{$parentPid}\n";
$childList = array();
 
// 创建共享内存,创建信号量,定义共享key
$shm_id = ftok(__FILE__,&#39;m&#39;);
$sem_id = ftok(__FILE__,&#39;s&#39;);
$shareMemory = shm_attach($shm_id);
$signal = sem_get($sem_id);
const SHARE_KEY = 1;
// 生产者
function producer(){
    global $shareMemory;
    global $signal;
    $pid = posix_getpid();
    $repeatNum = 5;
    for ( $i = 1; $i <= $repeatNum; $i++) {
        // 获得信号量
        sem_acquire($signal);
 
        if (shm_has_var($shareMemory,SHARE_KEY)){
            // 有值,加一
            $count = shm_get_var($shareMemory,SHARE_KEY);
            $count ++;
            shm_put_var($shareMemory,SHARE_KEY,$count);
            echo "({$pid}) count: {$count}\n";
        }else{
            // 无值,初始化
            shm_put_var($shareMemory,SHARE_KEY,0);
            echo "({$pid}) count: 0\n";
        }
        // 用完释放
        sem_release($signal);
 
        $rand = rand(1,3);
        sleep($rand);
    }
}
function createProgress($callback){
    $pid = pcntl_fork();
    if ( $pid == -1) {
        // 创建失败
        exit("fork progress error!\n");
    } else if ($pid == 0) {
        // 子进程执行程序
        $pid = posix_getpid();
        $callback();
        exit("({$pid})child progress end!\n");
    }else{
        // 父进程执行程序
        return $pid;
    }
}
// 3个写进程
for ($i = 0; $i < 3; $i ++ ) {
    $pid = createProgress(&#39;producer&#39;);
    $childList[$pid] = 1;
    echo "create producer child progress: {$pid} \n";
}
// 等待所有子进程结束
while(!empty($childList)){
    $childPid = pcntl_wait($status);
    if ($childPid > 0){
        unset($childList[$childPid]);
    }
}
// 释放共享内存与信号量
shm_remove($shareMemory);
sem_remove($signal);
echo "({$parentPid})main progress end!\n";
ログイン後にコピー

3. シグナル

シグナルはシステムコールです。通常、私たちが使用する kill コマンドは、特定のプロセスに特定のシグナルを送信することです。 liunx/mac で kill -l を実行すると、特定のシグナルを確認できます。次の例では、親プロセスは 5 秒待機して、子プロセスに signint シグナルを送信します。子プロセスは信号をキャプチャし、信号処理関数で処理します。

$parentPid = posix_getpid();
echo "parent progress pid:{$parentPid}\n";
 
// 定义一个信号处理函数
function sighandler($signo) {
    $pid = posix_getpid();
    echo "{$pid} progress,oh no ,I&#39;m killed!\n";
    exit(1);
}
 
$pid = pcntl_fork();
if ( $pid == -1) {
    // 创建失败
    exit("fork progress error!\n");
} else if ($pid == 0) {
    // 子进程执行程序
    // 注册信号处理函数
    declare(ticks=10);
    pcntl_signal(SIGINT, "sighandler");
    $pid = posix_getpid();
    while(true){
        echo "{$pid} child progress is running!\n";
        sleep(1);
    }
    exit("({$pid})child progress end!\n");
}else{
    // 父进程执行程序
    $childList[$pid] = 1;
    // 5秒后,父进程向子进程发送sigint信号.
    sleep(5);
    posix_kill($pid,SIGINT);
    sleep(5);
}
echo "({$parentPid})main progress end!\n";
ログイン後にコピー

4. パイプ (名前付きパイプ)

パイプは、マルチプロセス通信の一般的に使用される手段であり、名前なしパイプは、関連する関係とのプロセス間通信にのみ使用できます。有名なパイプは、同じホスト上のどのプロセスでも使用できます。ここでは有名なチャンネルのみを紹介します。次の例では、子プロセスがデータを書き込み、親プロセスがデータを読み取ります。

// 定义管道路径,与创建管道
$pipe_path = &#39;/data/test.pipe&#39;;
if(!file_exists($pipe_path)){
    if(!posix_mkfifo($pipe_path,0664)){
        exit("create pipe error!");
    }
}
$pid = pcntl_fork();
if($pid == 0){
    // 子进程,向管道写数据
    $file = fopen($pipe_path,&#39;w&#39;);
    while (true){
        fwrite($file,&#39;hello world&#39;);
        $rand = rand(1,3);
        sleep($rand);
    }
    exit(&#39;child end!&#39;);
}else{
    // 父进程,从管道读数据
    $file = fopen($pipe_path,&#39;r&#39;);
    while (true){
        $rel = fread($file,20);
        echo "{$rel}\n";
        $rand = rand(1,2);
        sleep($rand);
    }
}
ログイン後にコピー

関連する推奨事項:

PHPプロセスロックの実装方法

PHPプロセス通信の注意点まとめ

PHPプロセス通信をベースにしたセマフォと共有メモリ通信

以上がPHPのプロセス間通信について詳しく解説の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

関連ラベル:
ソース:php.cn
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
最新の問題
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート
私たちについて 免責事項 Sitemap
PHP中国語ウェブサイト:福祉オンライン PHP トレーニング,PHP 学習者の迅速な成長を支援します!