管道
- 管道是Unix中最古老的进程间通信的形式。
- 我们把从一个进程连接到另一个进程的一个数据流称为一个“管道”
- 管道是半双工通信方式(指数据可以在一个信号载体的两个方向上传输,但是不能同时传输)
- 本质:操作系统在内核中创建的一块缓冲区
- 操作:通过IO接口实现管道这个缓冲区的操作
- 分类:匿名/命名管道
匿名管道
匿名管道,内核中没有名字的缓冲区
#include <unistd.h>
int pipe(int fd[2]);
- 功能:创建一无名管道
一个进程通过系统调用接口,在内核创建的一块没有明确标识的缓冲区,内核返回给创建的一块没有明确标识的缓冲区,内核返回给创建进程两个文件描述符供进程来操作管道;其中一个描述符用于从管道读数据,一个向管道中写入数据 - 参数
fd:文件描述符数组,其中fd[0]表示读端, fd[1]表示写端 - 返回值:成功返回0,失败返回错误代码
管道的读写特性:
1)若管道中没有数据,则read会阻塞,直到数据被写入(缓冲区中有数据)
2)若管道中数据满了,则write会阻塞,直到数据被读取(缓冲区有空闲空间)
3)若管道的所有读端被关闭,则write会触发异常,进程退出
4)若管道所有写端被关闭,则read会返回0
管道的read返回0,不仅仅指的是没有读到数据,-----所有写端被关闭
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <sys/wait.h>
int main() {
//ls | grep make
int pipefd[2];
int ret = pipe(pipefd);
if (ret < 0) {
perror("pipe error");
return -1;
}
int pid1 = fork();
if (pid1 == 0) {
//ls
close(pipefd[0]);
dup2(pipefd[1], 1); //将标准输出重定向到管道写入端
execlp("ls", "ls", NULL);
//如果替换失败直接退出
exit(0);
}
int pid2 = fork();
if (pid2 == 0) {
//grep make
close(pipefd[1]); //关闭所有写端,grep循环读取数据,read返回0,就不在读
dup2(pipefd[0], 0); //将标准输入端重定向到管道读取端
execlp("grep", "grep", "make", NULL);
exit(0);
}
close(pipefd[0]);
//父进程的写端也要关闭
close(pipefd[1]);
waitpid(pid1, NULL, 0);
waitpid(pid2, NULL, 0);
return 0;
}
匿名管道特性
- 只能用于具有共同祖先的进程(具有亲缘关系的进程)之间进行通信;通常,一个管道由一个进程创建,然后该进程调用fork,此后父、子进程之间就可应用该管道。
- 管道提供流式服务
- 一般而言,进程退出,管道释放,所以管道的生命周期随进程
- 一般而言,内核会对管道操作进行同步与互斥
- 管道是半双工的,数据只能向一个方向流动;需要双方通信时,需要建立起两个管道
命名管道
- 匿名管道应用的一个限制就是只能在具有共同祖先(具有亲缘关系)的进程间通信。
- 如果我们想在不相关的进程之间交换数据,可以使用FIFO文件来做这项工作,它经常被称为命名管道。
- 命名管道是一种特殊类型的文件
创建命名管道
- 命名管道可以从命令行上创建,命令行方法是使用下面这个命令:
mkfifo [filename]
#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char *pathname, mode_t mode);
- 参数:
pathname:管道文件名称
mode:管道文件权限 - 返回值:0 失败:-1
- 打开特性
若文件没有被已经以读的方式打开,则以O_WRONLY打开时会阻塞
若文件没有被已经以写的方式打开,则以O_RDONLY打开时会阻塞 - 读写特性与匿名管道相同
“fifo_write.c”
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <sys/stat.h>
#include <string.h>
int main() {
char* fifo = "./test.fifo";
int ret = mkfifo(fifo, 0664);
if (ret < 0) {
//如果文件存在,并不让它退出
if (errno != EEXIST) {
perror("mkfifo error");
return -1;
}
}
printf("open fifo-----\n");
int fd = open(fifo, O_WRONLY);
if (fd < 0) {
perror("open error");
return -1;
}
printf("fifo:%s open success!\n", fifo);
while (1) {
char buf[1024] = { 0 };
printf("i say:");
fflush(stdout);
scanf("%s", buf);
write(fd, buf, strlen(buf));
}
close(fd);
return 0;
}
“fifo_read.c”
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <sys/stat.h>
int main() {
char* fifo = "./test.fifo";
int ret = mkfifo(fifo, 0664);
if (ret < 0) {
if (errno != EEXIST) {
perror("mkfifo error");
return -1;
}
}
printf("open fifo-----\n");
int fd = open(fifo, O_RDONLY);
if (fd < 0) {
perror("open error");
return -1;
}
printf("fifo:%s open success!\n", fifo);
while (1) {
//字节流服务,睡眠5秒钟,将缓冲区数据一起读入
sleep(5);
char buf[1024] = { 0 };
read(fd, buf, 1023);
printf("peer say:%s\n", buf);
}
close(fd);
return 0;
}