管道的实现原理是内核使用循环队列机制,借助内核缓冲区(4K)实现
管道的局限性:
- 由于管道采用半双工的通信方式,所以数据只能在一个方向上流动,要不读要不写(读、写只能占一样)
- 数据一旦被读走,便不在管道中留存,只能读取一次
- 只能在有公共祖先的进程间使用管道
创建匿名管道
- 应用于有血缘关系的进程间通信
pipe
函数
函数原型
int pipe(int pipefd[2]);
- 返回值
成功返回0;失败返回-1 - 函数调用成功后,返回两个文件描述符(r/w),无需手动open,但需手动close
pipefd[0]
:读文件描述符
pipefd[1]
:写文件描述符
例子
- 父子进程间通信
- 父进程写:要关闭父进程的读数据端,防止父进程自己把数据再次读走
- 父进程读:要关闭父进程的写数据端
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<fcntl.h>
#include<string.h>
//管道只要规定好一端读,一端写即可,哪方读哪方写自己规定
//此处子进程读,父进程写
int main(){
int fd[2];
int ret = pipe(fd);
pid_t pid = fork();
if(pid == -1){
perror("子进程创建失败");
exit;
}else if(pid == 0){
close(fd[1]);//子进程读数据,关闭写文件的文件描述符,防止子进程再往里面写数据
char buf[1024];//此处读数据,没用循环是因为写的内容一次性可以读完
ret = read(fd[0],buf,sizeof(buf));
printf("读到的数据为;%s",buf);//将读到的数据打印到屏幕上
write(STDOUT_FILENO,buf,ret);//将读到的数据打印到屏幕上
}else{
close(fd[0]);//父进程写数据,关闭读数据的文件描述符,防止子进程再自己把读走
char *str = "hello world\n";
write(fd[1],str,strlen(str));
}
return 0;
}
创建有名管道fifo
特点
- 有名管道
- 在磁盘上有一个管道文件
- 伪文件,在磁盘大小永远为0
- 在内核中有一个对应的缓冲区
- 半双工的通信方式
应用场景
- 用于非血缘关系进程之间的通信
创建方式
-
命令
mkfifo 管道名
-
函数
int mkfifo(const char* pathname, mode_t mode);
-
参数
pathname
:管道文件名mode
:管道文件的权限
-
例子
写
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<fcntl.h>
#include<string.h>
int main(){
//判断管道文件是否存在,不存在创建
int ret = access("myfifo",F_OK);
if(ret == -1){
ret = mkfifo("myfifo",0666);
if(ret == -1){
perror("管道文件fifo创建失败");
exit(1);
}
}
int fd = open("myfifo",O_WRONLY);
if(fd == -1){
perror("文件打开失败");
exit(2);
}
char buf[] = "hello world\n";
while(1){
write(fd,buf,sizeof(buf));
sleep(1);
}
close(fd);
return 0;
}
读
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<fcntl.h>
#include<string.h>
int main(){
int fd = open("myfifo",O_RDONLY);
if(fd == -1){
perror("文件打开失败");
exit(1);
}else if(fd == 0){
printf("%d\n",fd);
}
char buf[1024];
int len;
while(1){
len = read(fd,buf,sizeof(buf));
write(STDOUT_FILENO,buf,len);
// sleep(1);
}
close(fd);
return 0;
}