概括
管道是Linux支持的最初Unix IPC形式之一,管道的实质是一个内核缓冲区,管道的作用正如其名,需要通信的两个进程在管道的两端,进程利用管道传递信息。管道对于管道两端的进程而言,就是一个文件,但是这个文件比较特殊,它不属于文件系统并且只存在于内存中。
[root@VM-0-13-centos ssh]# file fifo_x
fifo_x: fifo (named pipe)
管道依据是否有名字分为匿名管道和命名管道(有名管道),这两种管道有一定的区别。
匿名管道有几个重要的限制:
- 管道是半双工的,数据只能在一个方向上流动,A进程传给B进程,不能反向传递
- 管道只能用于父子进程或兄弟进程之间的通信,即具有亲缘关系的进程。
命名管道允许没有亲缘关系的进程进行通信。命名管道不同于匿名管道之处在于它提供了一个路径名与之关联,这样一个进程即使与创建有名管道的进程不存在亲缘关系,只要可以访问该路径,就能通过有名管道互相通信。
上PHP代码
PHP只提供了命名管道的封装。
父子进程管道通信(阻塞)
<?php
$file = "fifo_x";
if(!posix_access($file, POSIX_F_OK)){
// 如果没有 创建管道文件
if(posix_mkfifo($file, 0666)){
fprintf(STDOUT, "创建OK \n");
}
}
$pid = pcntl_fork();
if($pid == 0){
$fd = fopen($file, "r");
// 如果管道中没有数据 则会持续阻塞
$data = fread($fd, 5);
if($data){
fprintf(STDOUT, "子进程 pid = %d read:%s \n", getmypid(), $data);
}
exit(0);
}
$fd = fopen($file, "w");
$len = fwrite($fd, "hello", 5);
fprintf(STDOUT,"pid = %d len = %d \n", getmypid(), $len);
fclose($fd);
$pid = pcntl_wait($status);
if($pid > 0){
fprintf(STDOUT,"子进程退出 pid = %d \n", $pid);
}
父子进程管道通信(非阻塞)
<?php
$file = "fifo_x";
if(!posix_access($file, POSIX_F_OK)){
if(posix_mkfifo($file, 0666)){
fprintf(STDOUT, "创建OK \n");
}
}
$pid = pcntl_fork();
if($pid == 0){
$fd = fopen($file, "r");
// 设置非阻塞读
stream_set_blocking($fd, 0);
while (1){
$data = fread($fd, 5);
if($data){
fprintf(STDOUT, "子进程 pid = %d read:%s \n", getmypid(), $data);
exit(0);
}else{
fprintf(STDOUT, "子进程 pid = %d 没有数据 \n", getmypid());
}
sleep(1);
}
}
$fd = fopen($file, "w");
sleep(5);
$len = fwrite($fd, "hello", 5);
fprintf(STDOUT,"父进程 pid = %d len = %d \n", getmypid(), $len);
fclose($fd);
$pid = pcntl_wait($status);
if($pid > 0){
fprintf(STDOUT,"子进程退出 pid = %d \n", $pid);
}
读端关闭产生 SIGPIPE 信号
<?php
$file = "fifo_x";
// 捕获产生的信号 回收进程 退出主进程
pcntl_signal(SIGPIPE, function ($signal){
fprintf(STDOUT, "进程 pid = %d 有信号 %d \n", getmypid(), $signal);
$pid = pcntl_wait($status, WNOHANG);
if($pid > 0){
fprintf(STDOUT,"子进程回收 pid = %d \n", $pid);
exit();
}
});
if(!posix_access($file, POSIX_F_OK)){
if(posix_mkfifo($file, 0666)){
fprintf(STDOUT, "创建OK \n");
}
}
$pid = pcntl_fork();
if($pid == 0){
$fd = fopen($file, "r");
$i = 0;
stream_set_blocking($fd, 0);
while (1){
$data = fread($fd, 5);
if($data){
$i++;
fprintf(STDOUT, "子进程 pid = %d read:%s \n", getmypid(), $data);
if($i == 5){ //读取五次 关闭子进程
break;
}
}
}
exit();
}
$fd = fopen($file, "w");
while (1){
pcntl_signal_dispatch();
$len = fwrite($fd, "hello", 5);
sleep(1);
}
非血缘进程通信
<?php
// 接收进程
$file = "fifo_x";
if(!posix_access($file, POSIX_F_OK)){
if(posix_mkfifo($file, 0666)){
fprintf(STDOUT, "创建OK \n");
}
}
$fd = fopen($file, "r");
// 非血缘进程通信 必须开启非阻塞
stream_set_blocking($fd, 0);
while (1){
$data = fread($fd, 128);
if($data){
fprintf(STDOUT, "接受 %s \n", $data);
}
}