1. 程式人生 > >形而上認識程序間通訊

形而上認識程序間通訊

(個人理解,有誤導歡迎指正)

道家說,形而上為道,形而下為器。人類為器,由道而來,人類可能永遠無法通徹宇宙之道,但計算機由人類所創,對計算機而言,人為道而計算器為器。我這裡只是在人類的角度上,形而上的認識電腦科學~

講解程序間通訊前,先了解一下計算機到底是什麼?

計算機本質上是為了方便解決人類問題而被人類創造而來的,說白了,是由可以理解和執行人類邏輯運算的CPU作為核心,外加一個匯流排,總線上掛載了一堆的裝置,記憶體、顯示卡、磁碟、網絡卡、定時器、中斷控制器等,這些裝置各有各的功能,比如儲存資料、產生電訊號、轉換資料為其他形式等,根源上講,這些裝置本質都是在處理、儲存、轉換電訊號,另外不同的裝置處理電訊號的速度不同,在各種外設中,要數記憶體RAM儲存電訊號速度最快,所以被設計來在程式執行過程中儲存CPU指令和資料的核心----任何儲存在較慢裝置上的CPU指令和資料在執行前都會被轉存到記憶體上進行執行。在所有的外設中,中斷控制器比較特殊,它可以產生電訊號來中斷CPU當前正在執行的指令,轉而去執行另外的指令,其他的好多外設也都可以產生中斷訊號,所有其他外設的中斷訊號都統一交給中斷控制器,由中斷控制器來中斷CPU,這樣設計很有擴充套件性~

作業系統是什麼?程序是什麼?

作業系統其實可以分為靜態部分和動態部分:

靜態部分是指經過編譯後的CPU指令,是一個在系統啟動的時候就被載入到記憶體中的大的函式庫,函式其實就是按功能劃分的一塊塊的CPU指令集合,函式庫裡包含了一堆API函式等待呼叫,這些被動的被呼叫的函式中,按功能可以分為很多種,比如,可以和各種外設打交道的函式稱為裝置驅動程式、可以操作記憶體的函式有記憶體管理函式、程序管理函式、可以處理外設中斷訊號的中斷處理函式等。

動態部分其實就是指作業系統執行起來後的記憶體佈局了,用程式執行上下文來描述應該比較合理,我們來分析下程式執行過程中都涉及到了哪些上下文:首先,指令執行過程會有CPU中的一系列暫存器參與來參與完成指令,這組暫存器俗稱硬體上下文,其次,由於CPU暫存器數量有限,程式執行還必須要有記憶體的輔助,一般來說,函式的執行需要有記憶體棧和堆,堆和棧俗稱軟體上下文,我們可以這麼描述程序的定義:靜態的指令程式碼和靜態資料+程式執行上下文(堆疊和暫存器)組成了一個程序~

程序在記憶體中的佈局是什麼樣子的呢?

磁碟中的可執行程式被載入到記憶體中後,一般會有這麼幾部分來儲存:

程式碼段:存放只讀的CPU指令集合

只讀資料段:存放從可執行檔案載入到記憶體中就不會變化的只讀資料

已初始化資料段:存放從可執行檔案載入到記憶體中的已初始化的變數資料

未初始化資料段: 存放未初始化的變數

堆區:程式執行過程中動態生成的資料

棧區:函式執行上下文,函式執行時壓入新棧幀,函式執行後銷燬,棧幀中存放函式的引數、區域性變數等資料

簡單分析下作業系統啟動過程:

任何系統都會有個初始化的過程,作業系統也不例外。計算機啟動,然後作業系統被從磁碟載入後,最先會啟動的第一個程序是idle程序,它要做如下一些事情:

1 初始化虛擬記憶體,核心頁表等

2 初始化VFS-虛擬檔案系統,VFS是核心中的一種檔案系統,所有外部裝置、外設上的其他檔案系統都會掛載到該檔案系統上,linux把計算機所有的外部裝置都抽象為檔案,俗稱裝置檔案,磁碟上的多個分割槽的檔案系統也會掛載到VFS上,該檔案系統是應用程序訪問外部裝置的和外部檔案系統的橋樑,說白了,VFS就是作業系統初始化後在記憶體中建立的檔案系統資料結構,檔案系統一般分為inode索引節點和其關聯的資料節點,資料節點可以對應為普通的二進位制資料,也可以和核心在記憶體中建立的其他資料結構相關聯,比如裝置檔案inode節點就會關聯一個結構體,結構體中包含了對指定裝置進行訪問的函式,比如open close read write等

3 孵化出init程序,通常init程序會裝入shell程式,shell程式可以是命令列的shell,比如bash,也可以是圖形介面的shell,比如Xserver,shell程式的主要功能是監聽終端裝置的命令,執行使用者程式,如bash監聽tty裝置,執行使用者程式,XServer配合視窗管理器提供桌面UI環境,監聽使用者的滑鼠和鍵盤事件,啟動GUI使用者程序等

4 初始化核心中斷向量表,允許硬體中斷,然後進入idle程序的事件迴圈。這時候正式開啟了程序排程,硬體定時器會定時產生中斷,該中斷會呼叫程序排程服務程式,來完成程序執行上下檔案的儲存和切換,實現程序排程。idle事件迴圈一般是執行計算機長時間無操作休眠以及呼叫程序排程服務程式來軟排程程序。後面就可以通過shell來執行更多的使用者程序了~

程序間如何通訊?

孤立的程序作用有限,大部分情況都會有多個程序間相互通訊的需求,我所說的程序間通訊是廣義的,不一定是在同一臺計算中中的程序,也可能是跨網路的多個計算機中的程序通訊。作業系統中的每個程序的地址空間都是相互隔離的,每個程序都有自己的頁表,指令、資料、堆疊都是相互隔離的,我們把兩個程序想象成兩個水槽,兩個水槽如何才能互相連通呢? 管道! 通過一根管道可以控制水流單向流動,一對管道就可以實現兩個水槽的水流互相流動。作業系統目前實現的所有程序間通訊方式都可以抽象成一個或者一對管道,來實現程序間的單向或者雙向通訊。

單機下的程序通訊方式有:

1 共享記憶體,一個程序通過系統呼叫在核心中開闢一塊共享的記憶體,然後和自己程序的頁表相關聯,然後開啟另一個子程序按同樣的方式將共享的那塊記憶體和自己的頁表相關聯來實現通訊,這裡共享記憶體充當了“管道”

2 管道檔案通訊,和共享記憶體的方式基本一致,通過系統呼叫在核心中開啟一個管道檔案,兩個程序一個讀管道、一個寫管道

3 訊息佇列,作業系統核心中提供一個訊息佇列服務,一個或多個程序向佇列生產資料,另一個程序或者多個程序消費同一個佇列的資料,這裡的佇列充當了我上面抽象的“管道”,而且可以多程序相互通訊,實現瞭解耦

4 socket檔案方式,兩個程序開啟一對socket檔案進行通訊,socket資料傳輸是雙向的,相當於我上面抽象的“雙向管道”,在單機的情況下,通過socket傳送的資料包不會經過網絡卡裝置傳送出去再接受回來,而是直接在記憶體協議棧中來回傳遞

跨機器的程序通訊方式:

由於跨了機器,本質上就是兩個程序是分別在各自機器的記憶體中執行的,由於是在兩個記憶體中,這種方式下的兩個程序要想通訊,只有一種方式來充當通訊“管道”了--socket,本質上,跨機器的socket通訊相當於一個機器的網絡卡外設和另一個機器的網絡卡外設之間通訊,然後分別在兩個機器上的程序分別讀取自己的socket檔案來把網絡卡外設的資料轉移到自己的記憶體程序中去,和單機間程序的通訊其實原理是一致的。

讓我們腦洞一下,多個機器間通過網路相連線,相當於是網線把每個機器的內部匯流排互相連線在一起了,假如科技的發展導致網線傳輸資料的速率達到了機器內部匯流排一樣的速度,那麼多個機器間就不存在本質的隔閡了,地球上只有一臺“超級計算器”!

我們再腦洞一下,單個計算機比作我們體內的神經元細胞,網絡卡和網線分別是樹突和軸突,多個計算機通過網線相連就相當於我們體內的神經網路,軸突和樹突依次相接組成龐大的網路,計算機之於人類會不會就像人類之於宇宙,細思恐極也~