管道(PIPE)是linux中一個重要的通信方式,在進程中,我們通過從一個進程中讀取到的數據轉到另一個進程中的寫數據中,這時就要有不同的進程之間共享同一份資源,就是所謂的進程間通信。由于進程的特點是資源獨占,所以我們就借助管道來實現。我們常說的管道多指無名管道,還有一種是命名管道(一次生成多個目標文件[FIFO])。下面分別講解一下:
管道的特點:單向數據通信,有血緣關系的進程通信,生命周期(隨進程的),同步與互斥
管道也是一種文件,它是以p開頭的;
我們可以調用pipe()函數:int pipe(int fileds[2])來在內核中開辟一條緩沖區(即管道)來完成通信。它有一個讀端一個寫端,fileds[0]表示讀端,fileds[1]表示寫端。首先,由父進程創建管道;其次,由父進程創建子進程;最后,父進程關閉fd[0]讀端,子進程關閉fd[1]寫端,這樣就使父進程往管道里寫,子進程往管道里讀,這樣就實現了進程間通信。
然而通過管道實現父子進程之間的通信步驟是什么呢?
代碼實現一下吧:
運行結果如下:
當子進程關閉讀端,父進程關閉寫端時,最后結果會每隔1s打印一個“i am a girl”;
一、對于管道來說我們通常有四種特殊情況需要考慮:
1.如果所有指向管道寫端的文件描述符都關閉了(管道寫端的引用計數等于0),而仍然有進程從管道的讀端讀數據,那
么管道中剩余的數據都被讀取后,再次read會返回0,就像讀到文件末尾一樣。
運行結果:
2.如果有指向管道寫端的文件描述符沒關閉(管道寫端的引用計數大于0),而持有管道寫端的進程也沒有向管道中寫數據,這時有進程從管道讀端讀數據,那么管道中剩余的數 據都被讀取后,再次read會阻塞,直到管道中有數據可讀了才讀取數據并返回。
運行結果:
此時,,會發現當運行完第十個i am a child之后,read端還想讀取時,發生了堵塞。直到關閉寫端后讀端才退出不再讀。
3.如果所有指向管道讀端的文件描述符都關閉了(管道讀端的引用計數等于0),這時有進程向管道的寫端write,那么該進
程會收到信號SIGPIPE,通常會導致進程異常終止。?
運行結果:
此時,在子進程進行寫了10個i am a child時,寫完之后寫端并沒有關閉,當父進程讀完三個i am a child之后,關閉讀端,讀端不再讀,并且sleep了10秒。進程異常中止。
4.如果有指向管道讀端的文件描述符沒關閉(管道讀端的引用計數大于0),而持有管道讀端的進程也沒有從管道中讀數
據,這時有進程向管道寫端寫數據,那么在管道被寫滿時再次write會阻塞,直到管道中有空位置了才寫入數據并返回。
運行結果:
此時,,寫端一直在寫,讀端沒有關閉,但卻不讀取數據,因此會導致write端阻塞,直到異常退出。
二、但是匿名管道只適用于具有血緣關系的進程之間通信。如果兩個進程之間沒有血緣關系的話怎么辦呢?我們把沒有血緣關系的進程之間的通信方式就可以通過命名管道來完成。創建命名管道的系統函數有:mknod和mkfifo。在這里,我們盡可能的使用mkfifo。命名管道也被稱為FIFO文件,它是一種特殊類型的文件,它在文件系統中以文件名的形式存在;下面舉一個兩個不同進程之間進行通信的例子:
server.c為讀端:
client.c為寫端:
將兩個不同的進程之間聯系起來,當client.c寫入數據時,打開另一個終端運行server.c,這樣就會在server.c中顯示出你輸入的字符串,當遇到quit時,進程將結束。
另外,通過命令ulimit -a來看一下管道寫入的最大大小為:
即:512*8 = 4kb;
以上就是管道的基本概念哦。