Linux进程通信之管道通信

2021-07-27 16:17:45 浏览数 (3)

父子进程管道通信

Linux进程通信的几种方式

  • 管道通信
  • 中断信号
  • 共享内存、消息队列
  • Unix Socket

我们PHP中所使用的workman、swoole 或者其他语言当中的进行通信也是无非以上的几种方式

阻塞代码

代码语言:javascript复制
$file = 'pipe_file';
//检测是否存在管道文件
if(!posix_access($file,POSIX_F_OK))
{
    //创建管道文件
    if(posix_mkfifo($file,0666))
    {
        fprintf(STDOUT,"create okrn");
    }
}

$pid = pcntl_fork();

//子进程
if($pid == 0)
{
    //以读方式打开文件
    $fd = fopen($file,'r');
    //读取五个字节
    $data = fread($fd,5);
    //读到数据则打印
    if($data){
        fprintf(STDOUT,"read press pid=%d recv:%sn",posix_getpid(),$data);
    }
    exit(0);
}
//以写方式打开文件
$fd = fopen($file,'w');
//写入五个字节
$len = fwrite($fd,'12345',5);

fprintf(STDOUT,"write process pid=%d,write len=%dn",posix_getpid(),$len);

fclose($fd);

//回收子进程,避免变成僵尸进程
$pid = pcntl_wait($status);
if($pid > 0)
{
    fprintf(STDOUT,"子进程退出成功 pid=%dn",$pid);

}

当通过运行代码时,我们可以发现的,当父进程写入数据后,子进程也会读到父进程写入的数据,但以上管道通信是以阻塞方式运行的,当没有数据时,进程则会阻塞不执行

非阻塞方式

代码语言:javascript复制
$file = 'pipe_file';
//检测是否存在管道文件
if(!posix_access($file,POSIX_F_OK))
{
    //创建管道文件
    if(posix_mkfifo($file,0666))
    {
        fprintf(STDOUT,"create okrn");
    }
}

$pid = pcntl_fork();

//子进程
if($pid == 0)
{
    //以读方式打开文件
    $fd = fopen($file,'r');
//非阻塞方式
    stream_set_blocking($fd,0);
    //读取五个字节
    $data = fread($fd,5);
    //读到数据则打印
    if($data){
        fprintf(STDOUT,"read press pid=%d recv:%sn",posix_getpid(),$data);
    }
    exit(0);
}
//以写方式打开文件
$fd = fopen($file,'w');
//非阻塞方式
stream_set_blocking($fd,0);
//写入五个字节
$len = fwrite($fd,'12345',5);

fprintf(STDOUT,"write process pid=%d,write len=%dn",posix_getpid(),$len);

fclose($fd);

//回收子进程,避免变成僵尸进程
$pid = pcntl_wait($status);
if($pid > 0)
{
    fprintf(STDOUT,"子进程退出成功 pid=%dn",$pid);

}

当加上函数stream_set_blocking以非阻塞方式运行后,会发现写进程并没有写进去,并且报了一个警告的错误,这个就是因为非阻塞模式,不管有没有接受到数据,都执行完毕退出导致的,下面我们再修改一下代码

非阻塞模式修改版

代码语言:javascript复制
$file = 'pipe_file';
//检测是否存在管道文件
if(!posix_access($file,POSIX_F_OK))
{
    //创建管道文件
    if(posix_mkfifo($file,0666))
    {
        fprintf(STDOUT,"create okrn");
    }
}

$pid = pcntl_fork();

//子进程
if($pid == 0)
{
    //以读方式打开文件
    $fd = fopen($file,'r');
    stream_set_blocking($fd,0);
    $i = 0;
    //循环读取数据,读到数据后才退出
    while (1)
    {
        $i  ;
        //打印循环了多少次
        echo $i.PHP_EOL;
        //读取五个字节
        $data = fread($fd,5);
        //读到数据则打印
        if($data){
            fprintf(STDOUT,"read press pid=%d recv:%sn",posix_getpid(),$data);
            break;
        }
    }

    exit(0);
}
//以写方式打开文件
$fd = fopen($file,'w');
stream_set_blocking($fd,0);
//写入五个字节
$len = fwrite($fd,'12345',5);

fprintf(STDOUT,"write process pid=%d,write len=%dn",posix_getpid(),$len);

fclose($fd);

//回收子进程,避免变成僵尸进程
$pid = pcntl_wait($status);
if($pid > 0)
{
    fprintf(STDOUT,"子进程退出成功 pid=%dn",$pid);

}

通过执行结果发现,到循环了1400多次后,写进程写入数据了,读进程读到数据后并退出了

注意

当读进程还在读数据的时候,写进程关闭,此时写进程则会无法写入数据,并且会发送一个中断信号SIGPIPE,此时需要自己进行处理。有想法的同学可以自己尝试写一下,自己实现信号调度并打印信号处理

0 人点赞