进程间通信IPC——管道(匿名管道)
程序员文章站
2024-03-01 13:13:22
...
1、概念
管道是个伪文件,本质上是内核使用环形队列机制,借助内核缓冲区实现。
2、特性
1)特征
- 管道本质为内核缓冲区,用户态来看是一个伪文件
- 由于是伪文件,所以对于管道有两个文件描述符印用,一个表示读端,一个表示写端
- 管道是基于字节流实现通信的
- 管道依赖于文件系统,其生命周期随进程
- 管道本身自带同步互斥
- fork()进程的时候,子进程会继承父进程的管道与其描述符
2)局限性
- 管道采用半双工通信方式,数据只能在单方向上流动
- 管道中数据不可反复读取。一旦读走,管道中不再存在
- 管道必须作用于有血缘关系的进程之间
注意:
由于管道是借助内核缓冲区(4k)实现。所以,向管道中写入数据的大小要控制不能超过 pipe_buf[ ] (4096个字节)
查看内核缓冲区大小通过 ulimit -a 命令查看
3、接口
UNIX中创建匿名管道的函数如下:
#include <unistd.h>
int pipe(int filedes[2]);
返回:
成功: 0
失败:-1
参数:
filedes[2]:文件描述符数组
通过调用pipe函数,管道会产生两个文件描述符:
- fds[0] 读取数据
- fds[1] 写入数据
从文件描述符角度看管道:
4、管道读写规则
1)读管道
- 管道中有数据,read返回实际读到的字节数。
- 管道中没有数据:
(1)管道写端被全部关闭,read读完管道中剩余数据后会返回0 (好像读到文件结尾)
(2)写端没有全部被关闭,read阻塞等待(不久的将来可能有数据递达,此时会让出cpu)
2)写管道
- 读端全部被关闭,写端执行写时进程会异常终止(也可使用捕捉SIGPIPE信号,使进程不终止)
- 管道读端没有全部关闭:
(1)管道已满,write阻塞
(2)管道未满,write将数据写入,并返回实际写入的字节数
5、示例
示例一:从键盘读取数据,写⼊入管道,读取管道,写到屏幕
/*************************************************************************
> File Name: pipe.c
> Describ: 从键盘读取数据,写入管道,读取管道,写到屏幕
> Author: hang
> Mail: aaa@qq.com
> Created Time: Wed 26 Feb 2018 11:03:30 PM CST
************************************************************************/
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
int main(void)
{
int fds[2];
char buf[100];
int len;
if(pipe(fds) == -1)
{
perror("make pipe");
exit(1);
}
//read from stdin
while(fgets(buf, 100, stdin)){
len = strlen(buf);
//写入管道
if(write(fds[1], buf, len) != len){
perror("write to pipe");
break;
}
memset(buf, 0x00, sizeof(buf));
//读取管道
if((len=read(fds[0], buf, 100)) == -1){
perror("read from pipe");
break;
}
//write to stdout
if(write(1, buf, len) != len){
perror("write to stdout");
break;
}
}
return 0;
}
程序运行结果如下:
示例二:使用管道实现父子进程间通信,完成:ls | wc –l。假定父进程实现ls,子进程实现wc。
/*************************************************************************
> File Name: pipe_ls_wc.c
> Describ: 使用管道实现父子进程间通信,完成:ls | wc –l
> Author: hang
> Mail: aaa@qq.com
> Created Time: Wed 26 Feb 2018 11:12:30 PM CST
************************************************************************/
// 利用管道进行通信
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
int main(void)
{
int fds[2];
if(pipe(fds) < 0) // 创建管道
{
perror("pipe error");
exit(1);
}
pid_t pid = fork(); // 创建子进程
if(pid<0)
{
perror("fork error");
exit(1);
}else if(pid==0) // 子进程
{
close(fds[0]); // 关闭读
dup2(fds[1], STDOUT_FILENO);// 输出定位到管道的写端
close(fds[1]); // 关闭写端
execlp("ls", "ls", NULL); // 用ls程序替换当前进程
perror("execlp error");
exit(1);
}else
{
close(fds[1]); // 关闭管道的写端
dup2(fds[0], STDIN_FILENO);// 将父进程的输入重定向到管道的读端
close(fds[0]); // 关闭管道的读端
execlp("wc", "wc", "-l", NULL); // wc替换
perror("execlp error");
exit(1);
}
}
程序运行结果如下:
分析: