爲Linux應用程序的開發人員,對Linux的進程間通訊方式確定是瞭如指掌,平時的開發中應該會大量的使用到。當你迅速的在鍵盤上按下【CTRL+C】終止掉一個正在運行中的命令時,你有沒有仔細的思考過背後的原理呢?或者是他們是經過什麼通訊方式呢?這個通訊方式是怎樣實現的呢?本文就帶着你們去Linux進程間通訊一探究竟,找出他們的原理。node
咱們都知道,應用程序在運行起來以後(進程),是相互獨立的,都有本身的進程地址空間。可是每每在一些業務上須要進程間的通訊,來完成系統的某個完整的功能。咱們來看下進程間通訊能幹那些事情?首先固然最重要的是:數據結構
進程間的通訊方式通常能夠分爲八種,以下:函數
八種通訊方式設計
他們在不一樣的標準都有不一樣的實現,以下圖所示:blog
通訊方式框圖索引
咱們先從管道開始講起來吧。接口
這裏說的管理特指的是無名管道,它是一種半雙工的通訊方式。也就是說數據只能單向流動,通常是在具備親緣關係的進程間使用,好比父子進程。當一個進程建立了一個管道,並調用fork建立本身的一個子進程後,父進程關閉讀管道端,子進程關閉寫管道端,這樣提供了兩個進程之間數據流動的一種方式。隊列
管道是怎麼通訊的呢?進程
首先管道是內核的一個緩衝區,並且是在內存中。管道一頭鏈接着一個進程的輸出,另外一頭鏈接着另外一個進程的輸入。一個緩衝區不須要很大,它被設計成爲環形的數據結構,以便管道能夠被循環利用。當管道中沒有信息的話,從管道中讀取的進程會等待,直到另外一端的進程放入信息。當管道被放滿信息的時候,嘗試放入信息的進程會等待,直到另外一端的進程取出信息。當兩個進程都終結的時候,管道也自動消失。看下圖:事件
那管道怎樣創建的呢?
從原理上,管道利用fork機制創建,從而讓兩個進程能夠鏈接到同一個PIPE上。最開始的時候,上面的兩個箭頭都鏈接在同一個進程1上(鏈接在進程1上的兩個箭頭),以下圖。當fork複製進程的時候,會將這兩個鏈接也複製到新的進程(進程2)。隨後,每一個進程關閉本身不須要的一個鏈接 (兩個黑色的箭頭被關閉; 進程1關閉從PIPE來的輸入鏈接,進程2關閉輸出到PIPE的鏈接),這樣,剩下的紅色鏈接就構成了如上圖的PIPE。
管道在內核中具體怎麼實現的呢?
在Linux內核中,並無針對管道新增數據結構。而是巧妙的借用了文件系統的file結構和虛擬文件系統的索引節點inode。經過將兩個 file 結構指向同一個臨時的 VFS 索引節點,而這個 VFS 索引節點又指向一個物理頁面而實現的。
具體實現
管道的實現其實也不難,源代碼在內核工程中的fs/pipe.c,咱們着重的講兩個比較重要的接口吧,也就是經常使用的pipe_read()和pipe_write(),前者是管道讀函數,後者是管道寫函數。
管道寫函數將字節數據複製到虛擬文件系統索引節點指向的物理內存頁,而讀函數則是相反:讀出數據。固然這裏存在着競爭的管理,須要必定的同步機制,使用鎖,等待隊列和信號。
當咱們寫的時候,調用write(),內核根據傳入的文件描述符fd,找到該文件的file結構。而後執行結構中f_op中的寫函數。寫函數在向內存寫入數據以前,必須首先檢查虛擬文件系統索引節點信息,檢查是否有足夠的內存空間能夠寫和內存沒有被讀程序鎖定這兩個條件,只有知足了,才真正的進行內存拷貝。
接下來寫函數就會鎖定內存,而後複製數據到內存,不然就在虛擬文件系統inode的等待隊列中。
管道的讀取過程和寫入過程相似。可是,進程能夠在沒有數據或內存被鎖定時當即返回錯誤信息,而不是阻塞該進程,這依賴於文件或管道的打開模式。反之,進程能夠休眠在索引節點的等待隊列中等待寫入進程寫入數據。當全部的進程完成了管道操做以後,管道的索引節點被丟棄,而共享數據頁也被釋放。
因爲篇幅的限制,八大進程間通訊只講了管道,文章開頭的疑問也只能等到後續文章了,也算是留個懸念吧。