好久没写技术文章了,最近一直在研究DEFI,做投资的话还是需要研究下的相关板块好确定自己的投资思路,题外话。

回到正文,今天分享下PHP的匿名管道通信。

在开始之前,我们先了解下什么是匿名管道:

具体可以看这篇文章:linux进程间通信

现在,假设你已经充分了解了管道及匿名管道。我们知道,匿名管道相比较有名管道有一个最大的区别就是只能用在具有血缘关系的进程之间。前面,我们对于子进程的创建及使用做了简单的介绍,不过,今天,本篇文章用的并不是前面几篇文章所有的内容。

本篇文章的主角的是函数 popen ,它可以打开一个子进程,同时返回父进程与子进程之间的通信管道,而这个管道就是匿名的通信管道。具体实现如下:

首先,我们创建一个PHP文件 pipe-process.php ,内容如下:

<?php

$pid = getmypid();

$i = 5;
while ($i-->0) {
    // 非阻塞
    echo 'PID:'.$pid. '|当前时间:' . date('Y-m-d H:i:s')."\n";
    sleep(3);
}

很简答,就是分五次输出输出当前时间,每次间隔3秒。

然后,我们在创建一个 pipe1.php 文件:

<?php

$pid = getmypid();
echo 'PID:'.$pid."\n";


// 匿名管道通信
$pipe = popen('/usr/local/opt/php@7.2/bin/php /Users/xiaoteng/work/test/pipe-process.php', 'r');

while (1) {
    $line = fgets($pipe);
    echo $line;
}

该文件,利用 popen 函数打开一个子进程,子进程运行的就是上面的 pipe-process.php 文件,该函数返回的管道指针,我们可以利用io函数读取内容,利用 fgets 每次读取一行内容。

我们来执行下:

~ php pipe1.php
PID:82530
PID:82531|当前时间:2020-08-08 06:12:31
PID:82531|当前时间:2020-08-08 06:12:34
PID:82531|当前时间:2020-08-08 06:12:37
PID:82531|当前时间:2020-08-08 06:12:40
PID:82531|当前时间:2020-08-08 06:12:43

我们来看下进程的父子关系:

pstree -p 82530
-+= 00001 root /sbin/launchd
 \-+= 01439 xiaoteng /Applications/iTerm.app/Contents/MacOS/iTerm2 -psn_0_12291
   \-+= 71505 xiaoteng /Applications/iTerm.app/Contents/MacOS/iTerm2 --server l
     \-+= 71506 root login -fp xiaoteng
       \-+= 71507 xiaoteng -zsh
         \-+= 82530 xiaoteng php pipe1.php
           \--- 82531 xiaoteng /usr/local/opt/php@7.2/bin/php /Users/xiaoteng/

子进程82531的直接echo输出是无法再父进程82530的命令行界面显示的,只要父进程中通过popen函数返回的管道资源读取,才能读取并输出子进程的echo内容。

上面演示的父进程读取子进程的数据,下面演示下父进程将数据写入到子进程:

子进程php文件内容:

<?php

$pid = getmypid();

while (1) {
    // 非阻塞
    stream_set_blocking(STDIN, 0);
    echo 'PID:'.$pid. '|当前时间:' . date('Y-m-d H:i:s'). '|读取内容:'.fread(STDIN, 1024)."\n";

    sleep(1);
}

父进程:

<?php

$pid = getmypid();
echo 'PID:'.$pid."\n";

// 匿名管道通信
$pipe = popen('/usr/local/opt/php@7.2/bin/php /Users/xiaoteng/work/test/pipe-process.php', 'w');

while (1) {
    fwrite($pipe, "父进程写入时间:".date('Y-m-d H:i:s')."\n");
    sleep(3);
}

程序都很简单,细心的小伙伴注意道,这里的函数 popen 第二个参数变了,前面的 popen 函数第二个参数是 r ,这里是 w 。其实一个代表的着 ,一个代表着 ,这个不难理解。

我们来运行下:

~php pipe1.php
PID:82738
PID:82739|当前时间:2020-08-08 06:19:20|读取内容:父进程写入时间:2020-08-08 06:19:20

PID:82739|当前时间:2020-08-08 06:19:21|读取内容:
PID:82739|当前时间:2020-08-08 06:19:22|读取内容:
PID:82739|当前时间:2020-08-08 06:19:23|读取内容:父进程写入时间:2020-08-08 06:19:23

PID:82739|当前时间:2020-08-08 06:19:24|读取内容:
PID:82739|当前时间:2020-08-08 06:19:25|读取内容:

从结果可以看到子进程可以读取父进程传递过来的信息。

到这里的话,小伙伴对于PHP的匿名管道通信应该有了一个简单的认识。心思缜密的小伙伴们可能关注到了,STDIN是什么?可以同时读取和写入数据吗?

STDIN是标准输入,这个后面我会写一篇文章介绍下。

popen 函数只能实现单向的传输,也就是同一时间,要不读要不写,只能有一个存在。至于这个读或者写是由它的第二个参数决定的。如果是读则返回的是 STDOUT 也就是标准输出,如果是写则返回 STDIN 及标准输入。

那么如何实现读写的双向操作呢?
答案:使用 proc_open 函数。

小滕将会在接下来的文章讲述。