管道是Linux进程间通讯的一种手段,管道本质其实就是一个文件。管道的类型有两种:
- 无名管道:用于父子进程的通信,或者兄弟进程之间的通信。
- 有名管道:用于两个独立的进程间通信,有名管道也叫 FIFO。
1、无名管道
通过man 2 pipe
可以查看Linux手册中关于pipe介绍:
#include <unistd.h>
int pipe(int pipefd[2]);
通过 pipe(int pipefd[2])
函数可以创建一个无名管道,无名管道是一种半双工(单向)的进程间通信方式。数组pipefd用于返回两个引用管道末端的文件描述符。
其中,pipefd[0]表示管道的读取端,pipefd[1]表示管道的写入端。写入管道写入端的数据由内核缓冲,直到读取端读取为止。在写入端需要关闭pipefd[0],在读取端需要关闭pipefd[1]。
返回值:0代表成功,-1代表失败。
1.1 代码示例
# include <unistd.h>
# include <stdio.h>
int main(){
int pipefd[2];
int res= pipe(pipefd);
if(res<0){
printf("create pipe fail");
return -1;
}
pid_t pid=fork();
if(pid == -1){
printf("创建子进程失败");
}else if(pid == 0){
//子进程写数据,因此关闭读
close(pipefd[0]);
write(pipefd[1],"hello,i'm your child proc\n",26);
}else{
//父进程读数据,关闭写
close(pipefd[1]);
char buff[30];
read(pipefd[0],buff, 26);
printf("父进程收到数据:%s",buff);
}
return 0;
}
1.2 无名管道小结
- 其本质是一个文件,但是其不在磁盘上,在内核缓冲区。
- 数据一旦被读走,便不在管道中存在,不可反复读。
- 采用半双工通信方式,如果需要双向通信可以创建两个管道。
- 由于无名管道没有名字,文件在文件系统中不可见,而用fork产生的子进程会自动继承父进程的描述符 ,也就是无名管道的读写端,因此只能在父子,兄弟进程间使用。
2、有名管道
man 3 mkfifo
查看Linux关于mkfifo系统函数
#include <sys/stat.h>
int mkfifo(const char *pathname, mode_t mode);
mkfifo函数是一个系统调用,一旦你用这种方式创建了一个FIFO特殊文件,任何进程都可以像普通文件一样打开它进行读写。但是,它必须在两端同时打开,然后才能继续对其执行任何输入或输出操作。打开FIFO进行读取通常会被阻塞,直到其他进程打开相同的FIFO进行写入,反之亦然。
参数:
- pathname:指定文件的path name
- mode:指定文件的权限
返回值:成功返回0,失败返回-1。
2.1 代码示例
vim fifoTest.c
代码如下
#include <sys/stat.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <fcntl.h>
int main(){
//创建一个fifo管道文件
int res=mkfifo("fifo_temp", 0666);
printf("进程已经启动...\n");
if(res==-1){
printf("create fifo file fail");
return -1;
}
//以只读的方式打开文件
int fd= open("fifo_temp",O_RDONLY);
if(fd == -1){
printf("open file fail");
return -1;
}
char buff[30];
read(fd,buff,30);
printf("收到其他进程数据:%s\n",buff);
close(fd);
return 0;
}
vim fifoTest2.c
代码如下
#include <sys/stat.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <fcntl.h>
int main(){
//以只写的方式打开文件
int fd= open("fifo_temp",O_WRONLY);
if(fd == -1){
printf("open file fail");
return -1;
}
write(fd,"你好呀!\n",8);
printf("即将退出进程\n");
close(fd);
return 0;
}
打开两个终端,分别编译 fifoTest.c、fifoTest2.c。先执行fifoTest,会创建有名管道文件,等待其他进程写入。然后执行fifoTest2往文件中写入数据。
需要注意,上面的代码,在另一个进程没有启动时都会阻塞,这是因为使用了O_RDONLY 、O_WRONLY
- O_RDONLY 以只读方式打开 FIFO,进程会阻塞,直到另一个进程往 FIFO 里面写数据
- O_WRONLY 只写方式打开 FIFO,进程会阻塞,直到另一个进程往 FIFO 里面读数据
- 如果不想阻塞可以使用O_RDWR 同时读写的方式打开 FIFO