linux管道

管道是單向的字節流,它將某個進程的標準輸出鏈接到另外一個進程的標準輸入。管道和有名管道是最先的進程間通訊機制之一,管道可用於具備親緣關係進程間的通訊,有名管道克服了管道沒有名字的限制,所以,除具備管道所具備的功能外,它還容許無親緣關係進程間的通訊。管道和有名管道的讀寫規則是在程序中應用它們的關鍵。  

管道相關的概念html

   在linux中管道是經過指向同一個臨時的VFS inode的兩個file數據結構來實現的,此VFSinode指向內存中的同一個物理頁面。這就隱藏了讀寫管道和讀寫普通文件的差異。管道是半雙工的,數據只能向一個方向流動;須要雙方通訊時,須要創建起兩個管道;只能用於父子進程或者兄弟進程之間(具備親緣關係的進程);
管道對於管道兩端的進程而言,就是一個文件,但它不是普通的文件,它不屬於某種文件系統,單獨構成一種文件系統,而且只存在與內存中。數據的讀出和寫入:一個進程向管道中寫的內容被管道另外一端的進程讀出。寫入的內容每次都添加在管道緩衝區的末尾,而且每次都是從緩衝區的頭部讀出數據。
node

管道的建立:
 #include
int pipe(int fd[2])
該函數建立的管道的兩端處於一個進程中間,在實際應用中沒有太大意義,所以,一個進程在由pipe()建立管道後,通常再fork一個子進程,而後經過管道實現父子進程間的通訊(所以也不難推出,只要兩個進程中存在親緣關係,這裏的親緣關係指的是具備共同的祖先,均可以採用管道方式來進行通訊)。
linux

管道的讀規則:shell

管道兩端可分別用描述字fd[0]以及fd[1]來描述,須要注意的是,管道的兩端是固定了任務的。即一端只能用於讀,由描述字fd[0]表示,稱其爲管道讀端;另外一端則只能用於寫,由描述字fd[1]來表示,稱其爲管道寫端。若是試圖從管道寫端讀取數據,或者向管道讀端寫入數據都將致使錯誤發生。通常文件的I/O函數均可以用於管道,如close、read、write等等。
從管道中讀取數據:若是管道的寫端不存在,則認爲已經讀到了數據的末尾,讀函數返回的讀出字節數爲0;當管道的寫端存在時,若是請求的字節數目大於PIPE_BUF,則返回管道中現有的數據字節數,若是請求的字節數目不大於PIPE_BUF,則返回管道中現有數據字節數(此時,管道中數據量小於請求的數據量);或者返回請求的字節數(此時,管道中數據量不小於請求的數據量)。管道寫端關閉後,寫入的數據將一直存在,直到讀出爲止。
數據結構

管道的寫規則:函數

向管道中寫入數據:向管道中寫入數據時,Linux將不保證寫入的原子性,管道緩衝區一有空閒區域,寫進程就會試圖向管道寫入數據。若是讀進程不讀走管道緩衝區中的數據,那麼寫操做將一直阻塞。spa

注:只有在管道的讀端存在時,向管道中寫入數據纔有意義。不然,向管道中寫入數據的進程將收到內核傳來的SIFPIPE信號,應用程序能夠處理該信號,也能夠忽略(默認動做則是應用程序終止)。對管道的寫規則的驗證1:寫端對讀端存在的依賴性
在向管道寫入數據時,至少應該存在某一個進程,其中管道讀端沒有被關閉,不然就會出現錯誤(管道斷裂,進程收到了SIGPIPE信號,默認動做是進程終止)對管道的寫規則的驗證2:Linux不保證寫管道的原子性驗證
寫入管道的數據量大於4096字節時,緩衝區的空閒空間將被寫入數據(補齊),直到寫完全部數據爲止,若是沒有進程讀數據,則一直阻塞。
htm

管道的侷限性 
只支持單向數據流;
只能用於具備親緣關係的進程之間;
沒有名字;
管道的緩衝區是有限的(管道制存在於內存中,在管道建立時,爲緩衝區分配一個頁面大小);
管道所傳送的是無格式字節流,這就要求管道的讀出方和寫入方必須事先約定好數據的格式,好比多少字節算做一個消息(或命令、或記錄)等等。
blog

有名管道相概念 FIFO進程

   從名字就能夠看出來它是支持先進先出的原則的。管道應用的一個重大限制是它沒有名字,所以,只能用於具備親緣關係的進程間通訊,在有名管道(namedpipe或FIFO)提出後,該限制獲得了克服。FIFO不一樣於管道之處在於它提供一個路徑名與之關聯,以FIFO的文件形式存在於文件系統中。這樣,即便與FIFO的建立進程不存在親緣關係的進程,只要能夠訪問該路徑,就可以彼此經過FIFO相互通訊(可以訪問該路徑的進程以及FIFO的建立進程之間),所以,經過FIFO不相關的進程也能交換數據。值得注意的是,FIFO嚴格遵循先進先出(firstin firstout),對管道及FIFO的讀老是從開始處返回數據,對它們的寫則把數據添加到末尾。它們不支持諸如lseek()等文件定位操做。

有名管道的建立

#include
#include
int mkfifo(const char * pathname, mode_t mode)

該函數的第一個參數是一個普通的路徑名,也就是建立後FIFO的名字。第二個參數與打開普通文件的open()函數中的mode參數相同。若是mkfifo的第一個參數是一個已經存在的路徑名時,會返回EEXIST錯誤,因此通常典型的調用代碼首先會檢查是否返回該錯誤,若是確實返回該錯誤,那麼只要調用打開FIFO的函數就能夠了。通常文件的I/O函數均可以用於FIFO,如close、read、write等等。

FIFO的打開規則:

有名管道比管道多了一個打開操做:open。若是當前打開操做是爲讀而打開FIFO時,若已經有相應進程爲寫而打開該FIFO,則當前打開操做將成功返回;不然,可能阻塞直到有相應進程爲寫而打開該FIFO(當前打開操做設置了阻塞標誌);或者,成功返回(當前打開操做沒有設置阻塞標誌)。
若是當前打開操做是爲寫而打開FIFO時,若是已經有相應進程爲讀而打開該FIFO,則當前打開操做將成功返回;不然,可能阻塞直到有相應進程爲讀而打開該FIFO(當前打開操做設置了阻塞標誌);或者,返回ENXIO錯誤(當前打開操做沒有設置阻塞標誌)。對打開規則的驗證參見附2。

有名管道的讀規則 
   約定:若是一個進程爲了從FIFO中讀取數據而阻塞打開FIFO,那麼稱該進程內的讀操做爲設置了阻塞標誌的讀操做。
若是有進程寫打開FIFO,且當前FIFO內沒有數據,則對於設置了阻塞標誌的讀操做來講,將一直阻塞。對於沒有設置阻塞標誌讀操做來講則返回-1,當前errno值爲EAGAIN,提醒之後再試。
   對於設置了阻塞標誌的讀操做說,形成阻塞的緣由有兩種:當前FIFO內有數據,但有其它進程在讀這些數據;另外就是FIFO內沒有數據。解阻塞的緣由則是FIFO中有新的數據寫入,不論信寫入數據量的大小,也不論讀操做請求多少數據量。
   讀打開的阻塞標誌只對本進程第一個讀操做施加做用,若是本進程內有多個讀操做序列,則在第一個讀操做被喚醒並完成讀操做後,其它將要執行的讀操做將再也不阻塞,即便在執行讀操做時,FIFO中沒有數據也同樣(此時,讀操做返回0)。若是沒有進程寫打開FIFO,則設置了阻塞標誌的讀操做會阻塞。
   注:若是FIFO中有數據,則設置了阻塞標誌的讀操做不會由於FIFO中的字節數小於請求讀的字節數而阻塞,此時,讀操做會返回FIFO中現有的數據量。

有名管道的寫規則 

   約定:若是一個進程爲了向FIFO中寫入數據而阻塞打開FIFO,那麼稱該進程內的寫操做爲設置了阻塞標誌的寫操做。對於設置了阻塞標誌的寫操做:當要寫入的數據量不大於PIPE_BUF時,Linux將保證寫入的原子性。若是此時管道空閒緩衝區不足以容納要寫入的字節數,則進入睡眠,直到當緩衝區中可以容納要寫入的字節數時,纔開始進行一次性寫操做。
   當要寫入的數據量大於PIPE_BUF時,Linux將再也不保證寫入的原子性。FIFO緩衝區一有空閒區域,寫進程就會試圖向管道寫入數據,寫操做在寫完全部請求寫的數據後返回。
對於沒有設置阻塞標誌的寫操做:

   當要寫入的數據量大於PIPE_BUF時,Linux將再也不保證寫入的原子性。在寫滿全部FIFO空閒緩衝區後,寫操做返回。

   當要寫入的數據量不大於PIPE_BUF時,Linux將保證寫入的原子性。若是當前FIFO空閒緩衝區可以容納請求寫入的字節數,寫完後成功返回;若是當前FIFO空閒緩衝區不可以容納請求寫入的字節數,則返回EAGAIN錯誤,提醒之後再寫。

小結:

   管道經常使用於兩個方面:(1)在shell中時常會用到管道(做爲輸入輸入的重定向),在這種應用方式下,管道的建立對於用戶來講是透明的;(2)用於具備親緣關係的進程間通訊,用戶本身建立管道,並完成讀寫操做。

   FIFO能夠說是管道的推廣,克服了管道無名字的限制,使得無親緣關係的進程一樣能夠採用先進先出的通訊機制進行通訊。管道和FIFO的數據是字節流,應用程序之間必須事先肯定特定的傳輸"協議",採用傳播具備特定意義的消息。要靈活應用管道及FIFO,理解它們的讀寫規則是關鍵。


引文來源  點擊打開連接

相關文章
相關標籤/搜索