ホームページ > バックエンド開発 > PHPチュートリアル > PHP のプロセスとプロセス間通信について 1 つの記事で学びましょう

PHP のプロセスとプロセス間通信について 1 つの記事で学びましょう

青灯夜游
リリース: 2023-04-10 09:02:01
転載
2855 人が閲覧しました

この記事では、PHP のプロセスとプロセス間通信について理解します。一定の参考値があるので、困っている友達が参考になれば幸いです。

PHP のプロセスとプロセス間通信について 1 つの記事で学びましょう

環境

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

pcntl 拡張子: メインプロセスの拡張子であり、待機中に完了プロセスが作成されます。 posix 拡張機能: プロセス ID の取得、プロセスの強制終了など、完全な posix 互換マシンの一般 API。 sysvmsg 拡張機能: システム v モードでプロセス間通信を実装するメッセージ キュー。 sysvsem 拡張機能: System v モードでセマフォを実装します。 sysvshm 拡張機能: システム v モードで共有メモリを実装します。 ソケット拡張: ソケット通信を実装します。 これらの拡張機能は Linux/Mac でのみ使用でき、Windows ではサポートされません。最後に、php のバージョンは 5.5 であることをお勧めします。

簡単な例

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

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

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

プロセス間通信 (IPC)

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

1. メッセージ キュー

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

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. セマフォと共有メモリ

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

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

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

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 シグナルを送信します。子プロセスは信号をキャプチャし、信号処理関数で処理します。

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 のプロセスとプロセス間通信について 1 つの記事で学びましょうの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

ソース:juejin.cn
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
最新の問題
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート