1、管道(pipe)
管道是進程間通信的主要手段之一。一個管道實際上就是個只存在於內存中的文件,對這個文件的操作要通過兩個已經打開文件進行,它們分別代表管道的兩端。管道是一種特殊的文件,它不屬於某一種文件系統,而是一種獨立的文件系統,有其自己的數據結構。根據管道的適用范圍將其分為:無名管道和命名管道。
● 無名管道
主要用於父進程與子進程之間,或者兩個兄弟進程之間。在linux系統中可以通過系統調用建立起一個單向的通信管道,且這種關系只能由父進程來建立。因此,每個管道都是單向的,當需要雙向通信時就需要建立起兩個管道。管道兩端的進程均將該管道看做一個文件,一個進程負責往管道中寫內容,而另一個從管道中讀取。這種傳輸遵循“先入先出”(FIFO)的規則。
● 命名管道
命名管道是為了解決無名管道只能用於近親進程之間通信的缺陷而設計的。命名管道是建立在實際的磁盤介質或文件系統(而不是只存在於內存中)上有自己名字的文件,任何進程可以在任何時間通過文件名或路徑名與該文件建立聯系。為了實現命名管道,引入了一種新的文件類型——FIFO文件(遵循先進先出的原則)。實現一個命名管道實際上就是實現一個FIFO文件。命名管道一旦建立,之後它的讀、寫以及關閉操作都與普通管道完全相同。雖然FIFO文件的inode節點在磁盤上,但是僅是一個節點而已,文件的數據還是存在於內存緩沖頁面中,和普通管道相同。
2、環形緩沖區
每個管道只有一個頁面作為緩沖區,該頁面是按照環形緩沖區的方式來使用的。這種訪問方式是典型的“生產者——消費者”模型。當“生產者”進程有大量的數據需要寫時,而且每當寫滿一個頁面就需要進行睡眠等待,等待“消費者”從管道中讀走一些數據,為其騰出一些空間。相應的,如果管道中沒有可讀數據,“消費者” 進程就要睡眠等待,具體過程如下圖所示。
2.1環形緩沖區實現原理
環形緩沖區是嵌入式系統中一個常用的重要數據結構。一般采用數組形式進行存儲,即在內存中申請一塊連續的線性空間,可以在初始化的時候把存儲空間一次性分配好。只是要模擬環形,必須在邏輯上把數組的頭尾相連接。只要對數組最後一個元素進行特殊的處理——訪問尾部元素的下一元素時,重新回到頭部元素。對於從尾部回到頭部只需模緩沖長度即可(假設maxlen為環形緩沖的長度,當讀指針read指向尾部元素時,只需執行read=read%maxlen即可使read回到頭部元素)。
2.2讀寫操作
環形緩沖區要維護寫端(write)和讀端(read)兩個索引。寫入數據時,必須先確保緩沖區沒有滿,然後才能將數據寫入,最後將write指針指向下一個元素;讀取數據時,首先要確保緩沖區不為空,然後返回read指針對應得元素,最後使read指向下一個元素的位置。讀寫操作偽代碼:
2.3判斷“滿”和“空”
當read和write指向同一個位置時環形緩沖區為空或滿。為了區別環滿和空,當read和write重疊的時候環空;而當write比read快,追到距離read還有一個元素間隔的時候,就認為環已經滿了。環形緩沖區原理圖如圖3所示。
4.linux內核中pipe的讀寫實現
Linux內核中采用struct pipe_inode_info結構體來描述一個管道。
其中,當pipe為空/滿時,采用等待隊列,該隊列使用自旋鎖進行保護。
用struct Pipe_buffer數據結構描述pipe的緩沖(buffer)