概述
管道是
UNIX
系统
IPC
的最古老的形式,所有的
UNIX
系统都提供此种通信。所谓的管道,也就是内核里面的一串缓存,从管道的一段写入的数据,实际上是缓存在内核中的,令一端读取,也就是从内核中读取这段数据。对于管道传输的数据是无格式的流且大小受限。对于管道来说,也分为匿名管道和命名管道,其中命名管道也被叫做 FIFO,下面则分别阐述这两种管道。
匿名管道
默认情况下,在
Shell
命令执行过程中,任何一个命令都有一个标准输入设备(键盘)、标准输出设备(显示器)和标准输出设备(显示器),使用管道
"|"
可以将两个命令连接起来,从而改变标准的输入输出方式,下面是在 Linux 端运行命令行的一个截图:
image-20210704161819420
上述命令中的意思也就是,将
ls
命令得到的结果作为
grep tags
命令的输入。
image-20210704162803903
连接输入输出的中间设备即为一个管道文件,综上,也就是说使用管道可以将一个命令的输出作为另一个命令的输入(在运行的时候,一个命令将创建一个进程),而这种管道是临时的,命令执行完毕之后就会自动消失,这类管道称为
无名管道
。
匿名管道例子
匿名管道在使用前要先创建,其函数的声明如下:
extern int pipe (int __pipedes[2]);
此函数的参数是一个整型数组,如果执行成功,pipe 将存储两个整型文件描述符于
__pipedes[0]
和
__pipedes[1]
中,他们分别指向管道的两端。如果系统调用失败,则返回 -1。
读无名管道,该函数的声明如下:
extern ssize_t read (int __fd, void *__buf, size_t __nbytes);
第一个参数
fd
为打开的文件描述符,
buf
为读出数据的存储位置,
nbytes
为读取数据的大小,调用 read 函数将从 fd 指向的文件描述符指定的打开文件中宏读
n
字节到
buf
指向的缓冲区内。
如果试图向已经填满的管道写入,系统会自动阻塞。一个管道不能同时被两个进程打开。
extern ssize_ t write(int __fd, __const void *__buf, size_t __n);
从
buf
指向的缓冲区中向管道中写入
nbytes
字节,且每次写入的内容都附件在管道的末端。
那要如何使用管道在两个进程之间通信呢,我们可以使用
fork()
创建子进程,创建的子进程会复制父进程的文件描述符,这样就做到了两个进程各有两个
fd[0]与fd[1]
,两个进程就可以通过各自的
fd
写入和读取同一个管道文件实现进程通信了,具体原理如下所示:
image-20210704170602297
具体的例子如下所示:
#include
#include
#include
int main(int argc, char *argv[])
{
pid_t pid;
int temp;
int pipedes[2];
char s[14] = "test message!";
char d[14];
if (pipe(pipedes) == -1) // 创建管道
{
perror("pipe");
exit(EXIT_FAILURE);
}
if (pid == fork() == -1)
{
perror("fork");
exit(EXIT_FAILURE);
}
else if (pid == 0) // 子进程
{
printf("now,write data to pipe\n");
if (write(pipedes[1], s, 14) == -1) // 写数据到管道
{
perror("write");
exit(EXIT_FAILURE);
}
else
{
printf("the written data is:%s\n",s);
exit(EXIT_SUCESS);
}
}
else if (pid > 0) // 父进程
{
slepp(2);
printf("now, read from pipe\n"
);
if ((read(pipedes[0], d, 14)) == -1)
{
perror("read");
exit(EXIT_FAILURE);
}
printf("the data from pipe is:%s\n",d);
}
return 0;
}
代码运行的结果如下所示:
image-20210704172243185
命名管道
命名管道又被称之为是 FIFO ,未命名的管道只能在两个相关的进程之间使用,而且这两个相关的进程还要又一个共同创建了他们的祖先进程,但是,通过 FIFO ,不相关的进程也能交换数据。
首先,介绍下是如何创建命名管道的:
extern int mkfifo (__const char *__path, __mode_t __mode);
mkfifo
会根据参数建立特殊的有名管道文件,该文件必须不存在,而参数
mode
为该文件的权限。
下面是一个使用命名管道进行进程间通信的例子,例子分为两个程序,分别是读部分和写部分,首先看先往管道写数据的代码,代码如下所示:
#include
#include
#include
#include
#include
#include
int main()
{
int fd;
// FIFO file path
char * myfifo = "/tmp/myfifo";
// Creating the named file(FIFO)
// mkfifo(, )
mkfifo(myfifo, 0666);
char arr1[80], arr2[80];
while (1)
{
// Open FIFO for write only
fd = open(myfifo, O_WRONLY);
printf("The fd is:%d\n",fd);
// Take an input arr2ing from user.
// 80 is maximum length
fgets(arr2, 80, stdin);
// Write the input arr2ing on FIFO
// and close it
write(fd, arr2, strlen(arr2)+1);
close(fd);
// Open FIFO for Read only
fd = open(myfifo, O_RDONLY);
// Read from FIFO
read(fd, arr1, sizeof(arr1));
// Print the read message
printf("User2: %s", arr1);
close(fd);
}
return 0;
}
然后是先往管道读数据的代码,代码如下所示:
#include
#include
#include
#include
#include
#include
int