進程間通信(IPC Interprocess Communication)
進程和進程之間的通信只能通過內核,在內核中提供一塊緩沖區進行通信。內核提供的這種機制叫做IPC
在進程間完成數據傳輸需要借助操作系統提供的特殊方法,如:文件(開銷較大,穩定性低)、管道(使用簡單)、信號(開銷最小)、共享內存(共享映射區,可以完成沒有血緣關系的進程之間的 通信)、消息隊列、本地套接字(復雜,較難,最穩定)、命名管道等。
管道
進程的內存在虛擬內存上互不相關,但是有血緣關系的進程在物理內存上,進程的內核區在同一片區域。離得很近。
管道是一種最基本的IPC機制。作用于有血緣關系的進程之間,完成數據傳遞。
特征:
- 本質是一個偽文件(實際上是內核緩沖區)
Linux操作系統中實際上有七中數據類型(-,d,l,s,b,p,c),其中只有文件、目錄和符號鏈接占用磁盤空間,符號鏈接當路徑不是非常長的時候保存在inode
中,其他類型都是偽文件,不占用實際存儲空間 - 由兩個文件描述符引用,一個一個表示讀端,一個表示寫端
- 規定數據從管道的寫端流入管道,從讀端讀出數據。
原理:管道實際上是內核使用環形隊列機制,借助內核緩沖區(4K)實現,即一般是八個扇區
圓形的磁盤被劃分為多個扇區,每個扇區512字節。一個文件最少占用一個扇區。
因為是由隊列實現的,所以我們不能進行多次讀取,而且只能是固定方向傳送文件。
局限性:
- 數據不能同時讀寫
- 數據一旦讀走便不在管道中存在,不可重讀讀取數據
- 數據采用半雙工通信方式(數據只能在一個方向流動的通信方式,還有雙向全雙工、雙向半雙工、單工通信)
- 只能在有公共祖先的進程(有血緣關系的進程)間使用管道
pipe函數
頭文件:#include<unistd.h>
函數聲明:int pipe(int pipefd[2]);
規定fd[0]
讀,fd[1]
寫
返回值:成功返回0,失敗返回-1,同時設置erron
為了保證管道的單向流動性需要在進程中關閉一部分文件描述符。
從管道中讀取數據時當管道中沒有數據將會自動阻塞,直到有數據寫入。
讀管道:
- 如果管道中有數據,則read返回實際讀到的字節數
- 如果管道中沒有數據,若寫端關閉,read返回0,如果仍有寫端打開,則阻塞等待寫入。
寫管道:
- 如果讀端全關閉,則進程異常終止(SIGPIPE信號)
- 如果讀端打開,若管道未滿,則寫入數據,返回寫入字節數。若管道已滿,則阻塞(這種情況很少出現)。
當寫進程向管道中寫入時,它利用標準的庫函數write(),系統根據庫函數傳遞的文件描述符,可找到該文件的 file 結構。file 結構中指定了用來進行寫操作的函數(即寫入函數)地址,于是,內核調用該函數完成寫操作。寫入函數在向內存中寫入數據之前,必須首先檢查 VFS 索引節點中的信息,同時滿足如下條件時,才能進行實際的內存復制工作:
1.內存中有足夠的空間可容納所有要寫入的數據;
2.內存沒有被讀程序鎖定。
如果同時滿足上述條件,寫入函數首先鎖定內存,然后從寫進程的地址空間中復制數據到內存。否則,寫入進程就休眠在 VFS 索 引節點的等待隊列中,接下來,內核將調用調度程序,而調度程序會選擇其他進程運行。寫入進程實際處于可中斷的等待狀態,當內存中有足夠的空間可以容納寫入 數據,或內存被解鎖時,讀取進程會喚醒寫入進程,這時,寫入進程將接收到信號。當數據寫入內存之后,內存被解鎖,而所有休眠在索引節點的讀取進程會被喚醒。
管道的讀取過程和寫入過程類似。但是,進程可以在沒有數據或內存被鎖定時立即返回錯誤信息,而不是阻塞該進程,這依賴于文件或管道的打開模式。反之,進程可 以休眠在索引節點的等待隊列中等待寫入進程寫入數據。當所有的進程完成了管道操作之后,管道的索引節點被丟棄,而共享數據頁也被釋放。
參考博客 linux管道詳解
獲取管道緩沖區的大小:ulimit -a
優點:實現手段簡單
缺點:單向通信、只有血緣關系進程間使用