管道
实现原理:内核借助环形队列机制,使用内核缓冲区实现。
特质:
1、伪文件
2、管道中的数据只能一次读取
3、数据在管道中,只能单向流动
局限性:
1、自己写,不能自己读
2、数据不可以反复读
3、半双工通信
4、血缘关系进程间可用
pipe函数
创建并打开管道
int pipe(int fd[2])
参数:fd[0]:读端;fd[1]:写端
返回值:成功:0;失败:0;
include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<errno.h>
#include<pthread.h>
#include<string.h>
void sys_err(const char* str)
{
perror(str);
exit(1);
}
int main(int argc, char* argv[])
{
pid_t pid;
int ret;
int fd[2];
char buf[1024];
ret=pipe(fd);
if(ret == -1)
sys_err("pipe error");
pid = fork();
if(pid > 0){
close(fd[0]); // 关闭读端
write(fd[1], "hello pipe", strlen("hello pipe"));
sleep(1);
close(fd[1]);
}else if(pid == 0){
close(fd[1]); // 关闭写端
ret = read(fd[0],buf, sizeof(buf));
write(STDOUT_FILENO, buf, ret);
close(fd[0]);
}
return 0;
}
管道的读写行为
读管道:
1、管道有数据,read返回实际读到的字节数
2、管道无数据:
1)无写端,read返回0(类似读到文件尾)
2)有写端,read阻塞等待
写管道:
1、无读端,异常终止
2、有读端:
1)管道已满,阻塞等待(SIGPIPE导致)
2)管道未满,返回写出字节个数
练习
使用管道实现父子进程通信,完成 ls | wc -l,假定父进程实现ls,子进程实现wc。
include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
int main(int argc, char* argv[])
{
pid_t pid;
int fd[2];
int ret;
ret=pipe(fd);
if(ret == -1)
perror("pipe error");
pid = fork();
if(pid==-1)
perror("fork error");
if(pid > 0){
close(fd[0]); // 父进程关闭读端
dup2(fd[1], STDOUT_FILENO); // 输出重定向,将标准输出重定向到管道写端
execlp("ls","ls",NULL);
close(fd[1]);
}else if(pid == 0){
close(fd[1]); // 子进程关闭写端
dup2(fd[0],STDIN_FILENO); // 输入重定向,将标准输入重定向到管道读端
execlp("wc", "wc", "-l", NULL);
close(fd[0]);
}
return 0;
}