822

PHP CLI启动多进程,并实现进程间通信

方式一:msg_get_queue实现

$key = ftok(__FILE__, 'a');
$queue = msg_get_queue($key);
$pidList = [];
$socketList = [];

for ($i = 1; $i <= 2; $i++) {
    $taskPid = pcntl_fork();
    if ($taskPid == -1) {
        die("[父进程]child{$i}创建失败");
    } elseif ($taskPid) {
        $pidList[$i] = $taskPid;
        echo "[父进程]child{$i}创建成功,子进程ID:{$taskPid}". PHP_EOL;
    } else {
        //子进程
        while (true) {
            msg_receive($queue, 1, $type, 1024, $msg);
            if ($msg) {
                echo "[子进程@child{$i}]收到消息:{$msg}";
            }
            usleep(50);
        }
    }
}

//父进程循环
while (true) {
    msg_send($queue, 1, ['from'=>'father','text'=>"Hi child,I'm father."]);
    sleep(3);
}

方式二:stream_socket_pair实现

$pidList = [];
$socketList = [];

for ($i = 1; $i <= 2; $i++) {
    $sockets = stream_socket_pair(STREAM_PF_UNIX, STREAM_SOCK_STREAM, STREAM_IPPROTO_IP);
    $mainSocket = $sockets[0];
    $taskSocket = $sockets[1];

    $taskPid = pcntl_fork();
    if ($taskPid == -1) {
        die("[父进程]child{$i}创建失败");
    } elseif ($taskPid) {
        fclose($mainSocket);
        $pidList[$i] = $taskPid;
        $socketList[$i] = $taskSocket;
        echo "[父进程]child{$i}创建成功,子进程ID:" . $taskPid . PHP_EOL;
    } else {
        //子进程
        while (true) {
            $text = fgets($mainSocket);
            if ($text) {
                echo "[子进程@child{$i}]收到消息:" . $text;
            }
            usleep(50);
        }
    }
}

//父进程循环
while (true) {
    foreach ($socketList as $i => $socket) {
        fwrite($socket, "Hi child{$i},I'm father.\n");
    }
    sleep(3);
}

完整方案,优雅退出

$pidList = [];
$socketList = [];

for ($i = 1; $i <= 2; $i++) {
    $sockets = stream_socket_pair(STREAM_PF_UNIX, STREAM_SOCK_STREAM, STREAM_IPPROTO_IP);
    $mainSocket = $sockets[0];
    $taskSocket = $sockets[1];

    $taskPid = pcntl_fork();
    if ($taskPid == -1) {
        die("[父进程]child{$i}创建失败");
    } elseif ($taskPid) {
        fclose($mainSocket);
        $pidList[$i] = $taskPid;
        $socketList[$i] = $taskSocket;
        echo "[父进程]child{$i}创建成功,子进程ID:" . $taskPid . PHP_EOL;
    } else {
        //子进程
        fclose($taskSocket);

        //子进程-开启异步信号处理
        pcntl_async_signals(true);
        pcntl_sigprocmask(SIG_BLOCK, [SIGTERM]);//阻塞SIGTERM信号

        //子进程-注册退出信号
        pcntl_signal(SIGQUIT, function () use ($mainSocket, $i) {
            fclose($mainSocket);
            echo "[子进程@child{$i}]收到退出信号,进程退出" . PHP_EOL;
            exit();
        });

        while (true) {
            $text = fgets($mainSocket);
            if ($text) {
                echo "[子进程@child{$i}]收到消息:" . $text;
            }

            usleep(50);
        }
    }
}


//父进程-开启异步信号处理
pcntl_async_signals(true);
pcntl_sigprocmask(SIG_BLOCK, [SIGTERM]);//阻塞SIGTERM信号

//父进程-注册退出信号
pcntl_signal(SIGQUIT, function () use ($socketList, $pidList) {
    foreach ($socketList as $socket) {
        fclose($socket);
    }
    echo '[父进程]收到退出信号,进程退出' . PHP_EOL;
    system('kill -QUIT ' . implode(' ', array_values($pidList)));
    foreach ($pidList as $pid) {
        pcntl_wait($status);
    }
    exit();
});

while (true) {
    foreach ($socketList as $i => $socket) {
        fwrite($socket, "Hi child{$i},I'm father.\n");
    }
    sleep(3);
}

使用kill -QUIT命令向父进程发送退出信号

kill -QUIT 父进程ID
文章作者:DOTATONG
发布日期:2023-03-09
# php

评论

暂无

添加新评论