一.管道的介绍
管道是进程间通信工具,管道包括无名管道和有名管道。前者在父子进程中流行,后者由于可独立成磁盘文件形式存在,能够被无血缘关系的进程共享管道是一种队列类型的数据结构,他的数据从一端输入,另一端输出。其最常见应用是连接两个进程的输入输出,即把一个进程的输出作为另一个进程的输入。
二.有名管道
有名管道可以在任意两个进程之间通信。
1. 有名管道的创建:
有名管道的创建:
- 命令创建: mkfifo + 管道名
- 系统调用创建
创建有名管道所需要的头文件:fcntl
2. 有名管道的使用举例:
进程 a 从键盘获取的数据循环传递给另一个进程 b。
我们可以用创建一个文件,a从键盘获取字符串存放到文件中,b从文件中读取。但是文件是存在磁盘上的,速度没有管道快,而且从文件读不到数据不会等待,但是管道不一样,b从管道读不到数据会阻塞,直到a又向管道中写入数据。
写端:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<assert.h>
#include<fcntl.h>
#include<sys/stat.h>
int main()
{
int fd = open("fifo",O_WRONLY);
assert(fd != -1);
printf("open FIFO success\n");
while(1)
{
printf("please input:\n");
char buff[128] = {
0};
fgets(buff,128,stdin);
write(fd,buff,strlen(buff)-1);
if(strncmp(buff,"end",3)==0)
{
break;
}
}
close(fd);
exit(0);
}
读端:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<assert.h>
#include<fcntl.h>
#include<sys/stat.h>
int main()
{
int fd = open("fifo",O_RDONLY);
assert(fd != -1);
printf("FIFO open success\n");
while(1)
{
char buff[128] = {
0};
int n = read(fd,buff,127);
if(n <= 0 || (strncmp(buff, "end", 3)==0))
{
break;
}
printf("%s\n",buff);
}
close(fd);
exit(0);
}
(1)如果读端或者写端先打开,那么将会在open阻塞住 ,直至另一端也打开管道。
(2)写端和读端都打开后,读端会在read处阻塞,写端向管道中写入数据后,读端才正常执行read,执行完继续在read阻塞。
(3)如果读端关闭,写端在向管道输入数据后会触发信号(SIGPIPE)自动退出。
(4)如果写端关闭,读端read函数值会返回0。
三.无名管道
无名管道主要应用于父子进程间的通信。
1.无名管道的创建
使用pipe()可以对无名管道进行创建 创建成功返回 0,失败返回-1
fds[0]是管道读端的描述符
fds[1]是管道写端的描述符
2.无名管道的使用举例
代码:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<assert.h>
int main()
{
int fd[2];
int res = pipe(fd);
assert(res != -1);
pid_t pid = fork();
assert(pid != -1);
if(pid == 0)
{
close(fd[1]);
char buff[128] = {
0};
read(fd[0],buff,127);
printf("child read:%s\n",buff);
close(fd[0]);
}
else
{
close(fd[0]);
char buff[128] = {
0};
printf("please input(less 128):\n");
fgets(buff,128,stdin);
write(fd[1],buff,strlen(buff)-1);
close(fd[1]);
}
exit(0);
}
四.管道的实现
写入从头指针开始,读取从尾指针开始。写入之后,头指针挪动,读取之后尾指针挪动。
如果是头指针赶上尾指针,那么管道被写满,写就会被阻塞。如果是尾指针赶上头指针,那么管道为空,read阻塞。
五.相关面试题
-
管道的通信方式是什么?怎么理解管道是这种通信方式?
管道的通信方式是半双工通信;
由于Linux一个命令只能完成一个功能,所以一个复杂点的任务需要好几个进程协同完成,第一个进程处理结果需交给第二个进程,然后一次交给第三个,等等,像流水线完成某个商品的生产一样,这个过程只需要数据单向往下传输,所以设计的时候做成了半双工。
当然也可以使用管道实现双工通信,需要两个管道。 -
三种通信方式是什么?有哪些区别?
单工通信:传输方向只有一个方向,单工通信只有一根数据线,它也只在一个方向上进行,如打印机、电视机等。
半双工通信:可以双向通信,但只能轮流传输,也只有一根数据线,不同于单工通信的是这根数据线即可作为发送又可作为接收,虽数据可在两个方向上传送,但通信双方不能同时收发数据。
全双工通信:可以同时双向传输数据,数据的发送和接收用两根不同的数据线,通信双方在同一时刻都能进行发送和接收,发送和接收同时进行,没有延迟。 -
有名管道和无名管道的区别是什么?
有名管道可以在任意两个进程间可以通信。
无名管道只能在父子进程间通信。 -
写入管道的数据存储在哪里?
写入管道的数据存储在内存中。