管道
管道是UNIX系统中最古老的IPC(进程间通信)方式,所有UNIX系统都提供这种通信机制。
管道就是从一个进程连接到另一个进程的一个数据流
管道分为命名管道和无名管道,在内核中申请一块固定大小的缓冲区,程序拥有写入和读取的权利,都可以看成一种特殊的文件,具有固定的读端和写端,也可以使用普通的read、write 等函数。但是它不是普通的文件,并不属于其他任何文件系统,并且只存在于内存中;无名管道一般使用fork函数实现父子进程的通信,命名管道用于没有血缘关系的进程也可以进程间通信;面向字节流、自带同步互斥机制、半双工(对讲机),单向通信,两个管道实现双向通信。
无名管道
完成进程间通讯,需要建立管道。管道并非属于进程的资源,而是和套接字一样,属于操作系统。
建立管道的函数
#include <unistd.h>
int pipe(int filedes[2]);
成功时返回0,失败时返回-1.
- filedes[0] 通过管道接收数据时使用的文件描述符,即管道出口。
- filedes[1] 通过管道传输数据时使用的文件描述符,即管道入口。
fd[1]永远是写端,而fd[0]永远是读端
管道示意图如下
下面给出无名管道的示例代码:
#include<stdio.h>
#include <unistd.h>
int main()
{
int fds[2];
char str[] = "I love lzr";
char buf[30];
pid_t pid;
pipe(fds);
pid = fork();
if(pid == 0){
write(fds[1], str, sizeof(str));
}
else{
read(fds[0], buf, 30);
puts(buf);
}
return 0;
}
根据fork函数有关内容请点击右侧 fork函数
运行结果如下图
但是你会发现,一个管道只会单向传递,如果我们想双向传递怎么办呢,我们可以建立两个管道,这样子就可以进行数据的双向传递。
概念图如下
示例代码如下:
#include<stdio.h>
#include <unistd.h>
int main()
{
int fds1[2], fds2[2];
char str1[] = "I love sx";
char str2[] = "I love lzr";
char buf[30];
pid_t pid;
pipe(fds1), pipe(fds2);
pid = fork();
if(pid == 0)
{
write(fds1[1], str1, sizeof(str1));
read(fds2[0], buf, 30);
printf("Child proc output : %s \n", buf);
}
else
{
read(fds1[0], buf, 30);
printf("Parent proc output : %s \n", buf);
write(fds2[1], str2, sizeof(str2));
sleep(3);
}
return 0;
}
命名管道
对比无名管道,无名管道只能用于父子之间的进程间通讯,而命名管道则不然,可以在不相关的进程间进行数据交换,FIFO本身是一种特殊的文件。
下面给出命名管道之间通讯的示例代码:
pipe4.c
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <assert.h>
int main(int argc, char** argv){
int fd = open("FIFO", O_WRONLY);
if (fd == -1){
printf("fd less than 0");
return 0;
}
while(1){
char buff[128] = {0};
fgets(buff, 127, stdin);
write(fd, buff, strlen(buff));
if(strncmp(buff, "end", 3) == 0){
break;
}
}
close(fd);
return 0;
}
pipe5.cpp
#include<stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#include <assert.h>
#include <iostream>
using namespace std;
int main(){
int fd = open("FIFO", O_RDONLY);
if(fd == -1){
cout << "fd less than 0" << '\n';
return 0;
}
char buff[128] = {0};
while(strncmp(buff, "end", 3) != 0){
read(fd, buff, 127);
cout << buff << '\n';
memset(buff, '\0', 128);
}
close(fd);
return 0;
}
运行结果如下图:
思路如图所示:
一定要在命令行加上上 mkfifo FIFO 这条语句,因为不然的话就会发生open错误,这也是我经历过的(也可以不加,不加的话就要在程序中创建FIFO文件)。但是这样子也只能实现单向通讯。这也是管道的特点,半双工的特性。其实就类似于对讲机,“地瓜地瓜我是土豆” “地瓜收到" ,只能一个人发送,一个人收到,这就是半双工,也就是单向通讯。
管道所能承担的最大字节数
不止这些,我自己测试了一下管道能承担的最大字节数。
示例代码如下:
#include <stdio.h>
#include <unistd.h>
int main()
{
int fds[2];
char str = 'a';
pid_t pid;
pipe(fds);
pid = fork();
if(pid == 0);
else
{
int count = 0;
while(1){
write(fds[1], &str, sizeof(str)); //往管道中写入一个字节
printf("%d\n", ++count); //实时输出已经容纳的字节
}
}
return 0;
}
运行结果如下:
由此可知,管道最大容纳的字节数为65536个字节,也就是64k,后面我上网搜索也是和我的验证结论一样,因此证明了我的压测结果没有错误。
以上就是我的总结的内容,如果问题,欢迎评论留言哦!