概述
管道是?UNIX系統(tǒng)?IPC的最古老的形式,所有的UNIX系統(tǒng)都提供此種通信。所謂的管道,也就是內(nèi)核里面的一串緩存,從管道的一段寫入的數(shù)據(jù),實際上是緩存在內(nèi)核中的,令一端讀取,也就是從內(nèi)核中讀取這段數(shù)據(jù)。對于管道傳輸?shù)臄?shù)據(jù)是無格式的流且大小受限。對于管道來說,也分為匿名管道和命名管道,其中命名管道也被叫做 FIFO,下面則分別闡述這兩種管道。
匿名管道
默認情況下,在?Shell命令執(zhí)行過程中,任何一個命令都有一個標(biāo)準(zhǔn)輸入設(shè)備(鍵盤)、標(biāo)準(zhǔn)輸出設(shè)備(顯示器)和標(biāo)準(zhǔn)輸出設(shè)備(顯示器),使用管道"|"可以將兩個命令連接起來,從而改變標(biāo)準(zhǔn)的輸入輸出方式,下面是在 Linux 端運行命令行的一個截圖:
image-20210704161819420
上述命令中的意思也就是,將ls命令得到的結(jié)果作為?grep tags命令的輸入。
image-20210704162803903
連接輸入輸出的中間設(shè)備即為一個管道文件,綜上,也就是說使用管道可以將一個命令的輸出作為另一個命令的輸入(在運行的時候,一個命令將創(chuàng)建一個進程),而這種管道是臨時的,命令執(zhí)行完畢之后就會自動消失,這類管道稱為無名管道。
匿名管道例子
匿名管道在使用前要先創(chuàng)建,其函數(shù)的聲明如下:
?
extern?int?pipe?(int?__pipedes[2]);
?
此函數(shù)的參數(shù)是一個整型數(shù)組,如果執(zhí)行成功,pipe 將存儲兩個整型文件描述符于__pipedes[0]和__pipedes[1]中,他們分別指向管道的兩端。如果系統(tǒng)調(diào)用失敗,則返回 -1。
讀無名管道,該函數(shù)的聲明如下:
?
extern?ssize_t?read?(int?__fd,?void?*__buf,?size_t?__nbytes);
?
第一個參數(shù)fd為打開的文件描述符,buf為讀出數(shù)據(jù)的存儲位置,nbytes為讀取數(shù)據(jù)的大小,調(diào)用 read 函數(shù)將從 fd 指向的文件描述符指定的打開文件中宏讀?n?字節(jié)到?buf?指向的緩沖區(qū)內(nèi)。
如果試圖向已經(jīng)填滿的管道寫入,系統(tǒng)會自動阻塞。一個管道不能同時被兩個進程打開。
?
extern?ssize_?t?write(int?__fd,?__const?void?*__buf,?size_t?__n);
?
從?buf指向的緩沖區(qū)中向管道中寫入nbytes字節(jié),且每次寫入的內(nèi)容都附件在管道的末端。
那要如何使用管道在兩個進程之間通信呢,我們可以使用?fork()創(chuàng)建子進程,創(chuàng)建的子進程會復(fù)制父進程的文件描述符,這樣就做到了兩個進程各有兩個fd[0]與fd[1],兩個進程就可以通過各自的fd寫入和讀取同一個管道文件實現(xiàn)進程通信了,具體原理如下所示:
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)?//?創(chuàng)建管道 ????{ ????????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)???//?寫數(shù)據(jù)到管道 ????????{ ????????????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; }
?
代碼運行的結(jié)果如下所示:
image-20210704172243185
命名管道
命名管道又被稱之為是 FIFO ,未命名的管道只能在兩個相關(guān)的進程之間使用,而且這兩個相關(guān)的進程還要又一個共同創(chuàng)建了他們的祖先進程,但是,通過 FIFO ,不相關(guān)的進程也能交換數(shù)據(jù)。
首先,介紹下是如何創(chuàng)建命名管道的:
?
extern?int?mkfifo?(__const?char?*__path,?__mode_t?__mode);
?
mkfifo會根據(jù)參數(shù)建立特殊的有名管道文件,該文件必須不存在,而參數(shù)mode為該文件的權(quán)限。
下面是一個使用命名管道進行進程間通信的例子,例子分為兩個程序,分別是讀部分和寫部分,首先看先往管道寫數(shù)據(jù)的代碼,代碼如下所示:
?
#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;? }
?
然后是先往管道讀數(shù)據(jù)的代碼,代碼如下所示:
?
#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;? }?
?
下面是代碼運行的一個結(jié)果:
i
mage-20210706132916572
說明一下,就是說當(dāng)運行?write程序的時候,會創(chuàng)建fifo文件,命名管道,然后,在?write文件中就執(zhí)行open操作,但是,這里存在的一個問題就是,因為在運行?write程序的時候,沒有進程打開讀端,也就阻塞了?open函數(shù)的運行,只有運行read操作,以讀的方式讀取管道的數(shù)據(jù),這樣才能使得write中的open函數(shù)繼續(xù)執(zhí)行。
綜上,也就是命名管道在進程中通信的一個例子。
小結(jié)
上述就是本次進程通信中關(guān)于管道的相關(guān)內(nèi)容,其中就包括匿名管道以及命名管道,他們之間存在著差別嗎,也各有各的應(yīng)用,本次的分享就到這里啦~
評論