概述
管道是?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 "); ????????if?(write(pipedes[1],?s,?14)?==?-1)???//?写数据到管道 ????????{ ????????????perror("write"); ????????????exit(EXIT_FAILURE); ????????} ????????else ????????{ ????????????printf("the?written?data?is:%s ",s); ????????????exit(EXIT_SUCESS); ????????} ????} ????else?if?(pid?>?0)?????//?父进程 ????{ ????????slepp(2); ????????printf("now,?read?from?pipe "); ????????if?((read(pipedes[0],?d,?14))?==?-1) ????????{ ????????????perror("read"); ????????????exit(EXIT_FAILURE); ????????} ????????printf("the?data?from?pipe?is:%s ",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 ",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?main()? {? ????int?fd1;? ????//?FIFO?file?path? ????char?*?myfifo?=?"/tmp/myfifo";? ????char?str1[80],?str2[80];? ????while?(1)? ????{? ????????//?First?open?in?read?only?and?read? ????????fd1?=?open(myfifo,O_RDONLY);? ????????printf("The?fd?is:%d ",fd1); ????????read(fd1,?str1,?80);? ????????//?Print?the?read?string?and?close? ????????printf("User1:?%s",?str1);? ????????close(fd1);? ????????//?Now?open?in?write?mode?and?write? ????????//?string?taken?from?user.? ????????fd1?=?open(myfifo,O_WRONLY);? ????????fgets(str2,?80,?stdin);? ????????write(fd1,?str2,?strlen(str2)+1);? ????????close(fd1);? ????}? ????return?0;? }?
?
下面是代码运行的一个结果:
i
mage-20210706132916572
说明一下,就是说当运行?write程序的时候,会创建fifo文件,命名管道,然后,在?write文件中就执行open操作,但是,这里存在的一个问题就是,因为在运行?write程序的时候,没有进程打开读端,也就阻塞了?open函数的运行,只有运行read操作,以读的方式读取管道的数据,这样才能使得write中的open函数继续执行。
综上,也就是命名管道在进程中通信的一个例子。
小结
上述就是本次进程通信中关于管道的相关内容,其中就包括匿名管道以及命名管道,他们之间存在着差别吗,也各有各的应用,本次的分享就到这里啦~
评论