管道
实现原理:一块内核中缓冲区,是环形队列
特性:
- 伪文件,因为他是内核缓冲区,所以占用的是内存,而不是磁盘空间
- 管道的数据只能读取一次
- 数据只能单向流动
局限性:
- 半双工
- 自己写,不能自己读
- 数据不能反复读取
- 只能用于有血缘关系的进程
pipe
int pipe(int fd[2])
参数:
- fd[0]:读端
- fd[1]:写端
返回值:
- 成功:0
- 失败:-1,设置errno
原理
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/wait.h>
void sys_err(const char* str){
perror(str);
exit(1);
}
int main(int argc,char* argv[]){
int ret;
int fd[2];
char buf[1024];
ret=pipe(fd);
if(ret==-1){
sys_err("pipe error");
}
pid_t pid=fork();
if(pid>0){ //父进程写
close(fd[0]); //父子进程在fork瞬间共享文件描述符,两者都拥有读写端,得把自己不用的去掉,才能形成单向导
通
write(fd[1],"hello",strlen("hello"));
close(fd[1]);
wait(NULL); //处理僵尸进
}else if(pid==0){ //子进程读
sleep(1); //防止变成孤儿进程
close(fd[1]); //关闭写端
ret=read(fd[0],buf,sizeof(buf));
write(STDOUT_FILENO,buf,ret); //把读的内容写在标准输出上
close(fd[0]);
}else if(pid<0){
sys_err("fork error");
}
return 0;
}
管道的读写行为
读管道:
- 管道有数据:read返回实际读到的字节数
- 管道无数据:(1)无写端,read返回0,相当于直接读到文件尾 (2)有写端,阻塞等待
写管道:
- 无读端:异常终止
- 有读端:(1)管道已满,阻塞等待(2)管道未满,返回写出的字节数
FIFO
一般来说,管道指的是pipe(匿名管道),用于有血缘关系的进程间通信,通过父子进程的fork的复制进行端口的传递。FIFO是有名管道,一般用于无血缘关系的进程间通信,可以主动找到这个FIFO文件的端口。
创建FIFO文件
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <stdlib.h>
int main(int argc,char* argv[]){
int ret=mkfifo("mytestfifo",0664);
if(ret==-1){
perror("mkfifo error");
exit(1);
}
return 0;
}
使用FIFO文件
就是按照读写文件来就是了,一个读文件,另一个写文件
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(int argc,char* argv[]){
int fd,i;
char buf[4096];
fd=open(argv[1],O_RDONLY);
if(fd<0){
perror("open error");
exit(1);
}
while(1){
int len=read(fd,buf,sizeof(buf));
write(STDOUT_FILENO,buf,len);
sleep(1);
}
close(fd);
}
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main(int argc,char* argv[]){
int fd,i;
char buf[4096];
fd=open(argv[1],O_WRONLY);
if(fd<0){
perror("open error");
exit(1);
}
i=0;
while(1){ //不断往管道文件内些数据
sprintf(buf,"hello itcast %d\n",i++);
write(fd,buf,strlen(buf));
sleep(1);
}
close(fd);
}
使用文件进行进程间通信
既然管道的进程间通信原理就是文件的读写,那可以直接用文件作为中介。他们的区别就是,文件是占据磁盘空间的,要读写磁盘,速度较慢。管道是伪文件,是内存的一片缓冲区,速度较快。
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
int main(int argc,char* argv[]){
//创建子进程
int pid=fork();
char buf[4096];
int fd=open("fifo_share",O_RDWR|O_CREAT|O_TRUNC,0644);
if(pid<0){
perror("fork error");
exit(1);
}else if(pid==0){ //子进程写
write(fd,"hello",strlen("hello"));
}else if(pid>0){ //父进程读
wait(NULL);
int len=read(fd,buf,sizeof(buf));
write(STDOUT_FILENO,buf,len);
}
}
有血缘关系的进程共享一个文件描述符,相当于共享一个打开的文件。 无血缘关系的进程只需要打开相同的文件。