进程间通信–管道
管道是一种最基本的 IPC 机制,作用于有有血缘关系的进程之间,完成数据传递。调用 pipe 系统函数即可创建一个管道,有如下特质:
- 其本质是一个伪文件(实为内核缓冲区)
- 由两个文件描述符引用,一个表示读端,一个表示写端
- 规定数据从管道的写端流入管道,从读端流出
管道的原理:管道实为内核使用环形队列机制,借助内核缓冲区(4k)实现。管道的局限性如下:
- 数据自己读不能自己写
- 数据一旦被读走,便不再管道中存在,不可反复读取
- 由于管道采用半双工通信方式。因此,数据只能在一个方向上流动
- 只能在有公共祖先的进程间使用管道(匿名管道, 命名管道不需要进程间具备公共祖先)
匿名管道
- 功能:创建一个无名管道
- 参数:
- fd:文件描述符数组,其中 fd[0] 表示读端,fd[1] 表示写端
- 返回值:成功返回 0,失败返回错误代码
失败原因:
- 文件描述符表满了,无法再创建文件描述符
- 管道使用完成之后,需要及时关闭文件描述符
注意:
管道中的数据一旦被读取之后就相当于出队列了到底是谁先读到,就取决于谁先执行读文件的操作。
管道内置了同步互斥机制,不会出现两个管道一人读一半数据的情况
1. 多个进程同事去读写管道,数据不会发生错乱。
- 如果管道为空,尝试读,就会在read函数处阻塞;如果管道满了,尝试写,就会在write处阻塞。
#include <iostream>
#include <unistd.h>
#include <cstdlib>
int main(void)
{
int pipefd[2];
if (pipe(pipefd) == -1)
{
perror("pipe");
return 1;
}
int ret = fork();
if (ret < 0)
{
perror("fork");
return 1;
}
else if (ret == 0) // child
{
close(pipefd[0]);
write(pipefd[1], "Hello", 5);
close(pipefd[1]);
exit(EXIT_SUCCESS);
}
else // parent
{
close(pipefd[1]);
char buf[1024] = {0};
read(pipefd[0], buf, 1023);
printf("child said: %s\n", buf);
}
return 0;
}
命名管道
命名管道与匿名管道最大的区别就是在于:
- 使用匿名管道通信的进程必须得具有共同的祖先,而命名管道不用。
命名管道的创建
可以在 shell 中创建
$ mkfifo filename
也可以在程序里创建
int mkfifo(const char* filename, mode_t mode)
创建命名管道
int main(void)
{
mkfifo("pipe", 0644);
return 0;
}
命名管道和匿名管道的区别
- 匿名管道由 pipe 函数创建并打开
- 命名管道有 mkfifo 函数创建,打开用 open
- 命名管道和匿名管道的打开方式不同外,其使用方式是相同的
?叮~