1. 程式人生 > >程序通訊、同步與排程

程序通訊、同步與排程

1. 程序通訊(Process Communication)
程序協作有其根本性的需求,主要是出於四點:資訊共享,通過並行提升計算速度,利於模組化以及方便性。因此也衍生出了程序通訊(IPC)的需求。下面將概述IPC的相關內容。
程序通訊用於程序之間的資料交換,主要有兩種抽象模型:其一共享記憶體模型(Shared memory),其二訊息傳遞模型(Message passing)。
顧名思義,採用共享記憶體的程序間通訊需要建立共享記憶體區域,它們通過在共享記憶體區域讀或寫來交換資訊。我們通過生產者—消費者模型對共享記憶體模型稍作研究。這個模型裡,生產者程序產生資訊以供消費者程序“消費”。為了能使這兩種程序併發執行,必須要有一個緩衝來被生產者填充併為消費者所使用。緩衝可以看作一個佇列,生產和消費可以分別看作入隊和出隊。這裡緩衝分為兩類,有限緩衝和無限緩衝,二者區別在於後者情況下,生產者無需等待,總是有緩衝區域存放其生產的訊息。
對於訊息傳遞模型,則不需要共享地址空間。訊息傳遞工具需要至少提供兩種操作:傳送訊息和接收訊息。而這兩種操作的關鍵在於通訊線路(Communication Link)。這裡我們只分析其邏輯實現,關注以下幾個問題。命名:在直接通訊中,需要通訊的每個程序必須明確的命名通訊的接收者或者傳送者。這種方案會在二者之間自動建立線路。但是這樣限制了程序定義的模組化。而在間接通訊中,通過郵箱或者埠來傳遞訊息。同步:訊息傳遞可以是阻塞非阻塞的,也稱為同步或者非同步。很容易理解,即程序是否會因為send()或者receive()而阻塞。緩衝:通訊交換的資訊駐留在臨時佇列裡,有三種實現方法:零容量,有限容量,無限容量。關鍵區別在於佇列的大小不同,對傳送者的阻塞情況不同,零容量必須阻塞傳送,有限容量會在佇列滿了的時候阻塞傳送,無限容量不會阻塞。
通過網路通訊的程序需要使用一對sockets作為端點,由一個iP地址和一個埠號組成。Socket一般採用客戶端-伺服器結構,伺服器通過監聽指定埠來等待客戶請求,收到請求後會自動連線。一般,1024以下的埠用於標準服務。

除此之外,程序通訊的方式還有管道(pipe)。管道分為普通管道和命名管道;兩者都是半雙工的。普通管道只能用於父子程序或兄弟程序間的通訊,因為普通管道通過fork呼叫來拷貝檔案描述符的,在檔案系統中,普通管道並不對應物理檔案。命名管道在檔案系統中有物理檔案存在,因此可以用於非親屬的程序間通訊。

2. 程序同步(Process Synchronization)
系統的不同部分操作資源,自然需要這些變化不相互影響,這是就需要程序同步。
總體來說,程序同步通過臨界區來實現,臨界區問題必須確保:互斥,前進,有限等待。臨界區前後分別由進入區和退出區,其它為剩餘區。
臨界區問題有兩類處理方法:搶佔核心和非搶佔核心。容易理解,非搶佔核心資料從根本上不會導致競爭條件,而搶佔核心情況下則複雜得多。但是考慮到後者在響應時間和實時程式設計的優勢,這種複雜值得花費力氣解決。這裡討論一些solution proposals。(a)使臨界區不可被打斷,與非搶佔核心類似,程序在臨界區內時不允許上下文切換,我們可以通過一個系統呼叫來實現這個需求。(b)嚴格輪換,但是忙等會浪費CPU資源。而且輪換如此嚴格,使連續多次執行某個程序的臨界區成為不可能。(c)對嚴格輪換進行改進得到Peterson’s solution,使用了兩個共享資料項int turn和boolean flag。但是同樣會導致忙等,而且可能會使程序的優先權錯位(d)在硬體上實現互斥鎖,同樣忙等。
實際最終我們選擇的方案是——訊號量。訊號量是一種資料型別,只能通過兩個標準原子操作訪問wait()和signal()。訊號量通常分為計數訊號量和二進位制訊號量,後者有時稱為互斥鎖。可以使用二進位制訊號量處理多程序的臨界區問題,而計數訊號量可以用來控制訪問具有若干例項的某種資源,此時訊號量表示可用資源的數量。
當一個程序位於臨界區時,其他試圖進入臨界區的程序必須在進入程式碼中連續地迴圈,這種稱為自旋鎖,會導致忙等。為了克服這一點可以修改wait()和signal()地定義——當一個程序執行wait()需要等待的時候,改為阻塞自己而不是忙等。阻塞操作將此程序放入到與訊號量相關的等待佇列中,狀態改為等待,然後會選擇另一個程序來執行。
考慮程序同步時,很重要的一點是避免死鎖。死鎖的特徵包括:互斥、佔有並等待、非搶佔、迴圈等待。當死鎖發生時,程序永遠不能完成,所以必須解決。有三種方法:(1)使用協議確保死鎖不發生(2)允許死鎖然後檢測恢復(3)認為死鎖不存在。

此外,程序同步中有三個經典問題,用來檢驗新的同步方案——生產者消費者問題、讀者-寫者問題、哲學家進餐問題,可以用來分析中的各種情況包括死鎖問題,這裡不作具體分析。

3. 程序排程
首先明確,程序執行由CPU執行和I/O等待週期組成,程序在這兩種狀態之間且切換。因為運算資源CPU是有限的,所以為了提高利用率,在CPU空閒時,必須從就緒佇列中選擇一個程序執行,具體到如何選擇,這就產生了排程。
排程應當滿足以下準則:CPU使用率和吞吐量最大化,週轉時間、等待時間和響應時間最小化。
稍微概述幾種排程演算法。
a) 先到先服務演算法:即先請求cpu的程序先分配到程序,實現簡單,但平均等待時間通常較長。考慮FCFS排程在動態情況下,會產生護航效果,會導致cpu和裝置使用率變得很低。
b) 最短作業優先排程:cpu空閒時,它會賦給具有最短cpu區間的程序,SJF演算法可證為最佳,其平均等待時間最小。但是困難在於如何知道下一個cpu區間的長度。一種方法是近似SJF排程,可以用以前cpu長度的指數平均來預測下一個區間長度。
c) 優先順序排程:每個程序都會有一個優先順序,具有最高優先順序的程序會被分配到cpu。這種演算法的主要問題是無窮阻塞或者飢餓,可以使用老化的方法來處理。
d) 輪轉法排程:專門為分時系統設計,與FCFS類似,但是增加了搶佔。這裡定義了一個時間片,就緒佇列作為迴圈佇列,每個程序分配不超過一個時間片的cpu。
e) 多級佇列排程:將就緒佇列分為多個獨立佇列,根據程序屬性每個佇列有自己的排程演算法。而且佇列之間必須有排程,通常採用固定優先順序搶佔排程。例如,前臺佇列可以比後臺佇列具有絕對的優先順序,這樣也符合互動的要求。
f) 多級佇列反饋排程:與多級佇列相比,差異在於允許程序在佇列之間移動。
如果在多個處理器的情況下,負載分配成為可能。一種方法是讓一個處理器處理所有排程決定,成為非對稱多處理。這種方法,資料共享的需要較小。另一種是對稱多處理(SMP),每個處理器自我排程。
此外,程序在不同處理器轉移時,第一個處理器快取必須無效,第二個處理器快取需要重構,這樣做代價太高,所以SMP系統儘量避免這樣做,試圖提高處理器親和性,努力使一個程序在用同一個處理器上執行。
為了更好的利用多處理器,更重要的一點是負載平衡,負載平衡通常有兩種方法:Push migration和Pull migration,篇幅所限,此不贅述。
參考文獻:
[1] Abraham Silberschatz. 作業系統概念. 高等教育出版社, 2007.3.