1. 程式人生 > >作業系統-併發控制原理及其實現

作業系統-併發控制原理及其實現

  首先我們要明白“皮之不存,毛將焉附”的道理,計算機系統是硬體與系統軟體完美結合的一個有機整體。因此在學習這一部分時,特別是學習中斷控制原理和系統凋用等內容時,要聯絡計算機組成原理的知識,這樣才能對整個系統瞭解。

一、程式和程序

  程序(process)這一術語 ,最初是在麻省理工學院(MIT)開發的MULTICS;系統以及IBM公司開發的CTSS/360系統中提出,時間是60年代初期。自那時起,程序已成為現代作業系統中最核心的概念之一。因為,作業系統的某本控制原理都是圍繞程序展開的。但是,程序控制的複雜性是由作業系統的併發機制引起的。 而目前經常使用的幾乎所有作業系統都支援並行或併發機制,我們先介紹併發控制,並以多道程式作為問題的切人點來引出程序的定義。

  注:這裡讀者可能還不太理解程序到底是怎麼回事?我們先把它記在心裡,回過頭再來看遍。

1.1  併發控制

  在支援多道程式環境的通用作業系統中 ,允許一個或若十個程序在系統中併發執行, 但由於系統硬體資源的有限性, 使得併發執行的若干程序之間會出現競爭系統有限軟硬體資源的現象。 這些資源包括處理器記憶體I/0裝置以及資料庫等, 這就需要作業系統來協調和優化分配系統共享資源。 特別是在單處理器系統中,任一時刻 CPU 只能執行一個程序, 而其他程序只能是等待 CPU 或其他資源。為了公平合理地對待所有程序, 核心為每個程序分配一小段時間一小段時間被稱為時間片。 一旦正在被執行程序的時間片用完, CPU 就立即被切換去執行另一個等待執行的就緒程序。由於 CPU 的執行速度很快, 造成若干個程序同時在執行的一 種虛擬假像。 這就是多道程式的併發控制。

1.1.1  多道程式設計與分時共享

  如上所述 ,作業系統的功能是管理系統資源,以便為使用者程式(應用程式)提供一個可執行環境。然而,對於初學者來說。作業系統卻是以一種複雜的令人難以琢磨的方式執行。 追根溯底 ,還要從計算機系統的硬體資源講起。

  在作業系統管理的硬體資源中,根據其工作速度可分為兩大類。一類是組成計算機主要功能部件的處理器CPU、快取記憶體、記憶體以及I/O介面;而另一類則是外圍裝置,又叫I/O裝置。包括:鍵盤顯示器印表機滑鼠軟碟機硬驅光碟機網路通訊裝置等。 在這兩類硬體資源中, 由於構成的原材料及工作性質的不同,其執行速度有著天壤之別,處理器 CPU 、快取記憶體以及記憶體等是由晶體半導體材料所組成;因此工作時, 其電訊號的傳播速度幾乎為光速。而外圍裝置大都含有機械裝置,顯然依賴按鍵、齒輪以及槓桿傳遞資訊,其執行速度無法與接近光速的電子器件相比。

  多道程式設計就是將記憶體分成若干個部分,每—部分存放不同程式的執行程式碼。當其中—個程序需要等待外設操作完成時,CPU可以儲存當前程序的所有資訊,選擇另—個程序來執行,但是如果多道程式中的一道是大型科學計算,在執行的數小時裡不需要外設操作,那麼記憶體中的其他若干道程式將只能等待,而擁有這些程式的使用者卻希望能及時得到響應。 這種需求導致了分時系統的出現。在分時系統中,系統為記憶體中的每一個程序分配一個時間片,當正在執行程式的時間片用完後,作業系統將把處理機分配給另一個就緒程序。對於單機系統(一個 CPU), 在某一時刻只能執行一個程序的一條指令,但是由於CPU的工作速度比人的反應快幾百萬倍甚至幾億倍。因此,雖然CPU在程序之間快速切換,而人的感覺卻是機器在同時執行多個程式。 這就是所謂的多道程式的分時共享,這種工作方式叫做併發機制。 當然,併發控制並非不用付出代價。當作業系統從一個程序切換到另一個程序時,也要使用CPU。因此併發機制首先要保證併發設計所帶來的效率要抵過由於程序切換所帶來的額外開銷。

  併發是指在同一時間間隔內對資源的共享。即記憶體中的多個程序分時共享CPU、記憶體以及 I/O裝置。顯然,併發機制可以高效地使用CPU,協調高速CPU與慢速外設的矛盾。但是處理併發並不容易。在記憶體中同時駐留多個程序需要特殊的硬體以及軟體的配合對其進行保護,以免各個程序的資訊被竊取並遭到攻擊。因此,現代作業系統的一個重要內容就是管理計算機的併發操作。

  作業系統中的許多迫切需要解決與研究的問題都是由併發機制而引起的。例如:圍繞若 “競爭條件”而引入了臨界區、原子操作、同步與互斥、鎖變數,等等。因此,併發控制向程式設計人員提出了新的重要的學習目標。因為併發程式並不總是按照預期的結果執行。因此除錯併發程式是一件棘手的事,而且併發程式典型的不易解決的毛病是,一個併發程式編譯執行後,大多數情況下執行結果都很好,但是極少數的情況下它會莫名其妙地失敗,而且無法找到原因。

1.1.2  併發控制的硬體支援

  作業系統要實現併發控制的設計目標,離不開硬體平臺的支援。因為核心必須使用中斷機制和硬體上下文切換機制,才能實現多道程式的分時共享,否則CPU無法知道某個程序的時間片已經耗盡,需要呼叫另一個程序執行。另外,還要考慮CPU執行程序切換的時間開銷要儘可能的小,如果CPU執行程序切換的時間開銷太大,則多道程式設計將失去意義。

  現代計算機都有一個叫做定時器和計數器的硬時鐘裝置,而用於定時中斷的時間測量裝置,叫做可程式設計間隔定時器。在IBM代機上一般使用8254CMOS晶片作為硬時鐘裝置,作業系統可對其進行程式設計,程式設計後它可以按照人的意願以一定的時間間隔向CPU發出定時中斷。也就是當間隔定時器到時時,它便產生一箇中斷請求,請求CPU去執行定時器中斷服務例程。該中斷服務例程將控制器交給作業系統,由作業系統確認當前程序在CPU上已執行的時間,如果分配給它的時間片已經用完,作業系統將剝奪該程序的執行權,並按照一定的演算法把它放置到就緒程序佇列中,等待下一個執行的機會。

  為了實現分時共享,核心必須將時間片已經耗盡的當前程序掛起,然後從就緒佇列中選擇,一個具有較高優先權的程序投入執行這個過程稱為程序切換,或叫任務切換、上下文切換

  在多道程式環境中雖然每個程序可以擁有屬於自己的地址空間,但所有程序必須共享使用,CPU的各種暫存器。因此在恢復一個程序執行之前.核心必須將該程序在掛起時的暫存器資料再裝人CPU的各個暫存器中才能繼續執行。我們把程序恢復執行前必須裝入暫存器的一組資料稱為硬體上下文。這樣可以節省程序切換為系統帶來的額外時間開銷,當然在具體實現程序切換時如果系統指標允許,也可以使用軟體的方法進行上下文切換。Linux2.2以上版本就是如此。

1.2  程序的定義和特徵

  在併發系統中,程序(process)是在由核心定義的資料結構上進行操作的一個計算活動。它是系統進行資源分配和排程的一個獨立單位。程序是動態的、有生命週期的。核心可以建立一個程序,並由排程程式“排程”而執行。在請求I/O操作時被阻塞,當它完成自己的歷史使命後,將由核心終止該程序使其消亡。

1.2.1  程式與程序

  程式這個術語是非常形象,非常具體的;它就是人們常說的“源程式”和“原始碼”。例如C語言經典程式:

1 #include <stdio.h>
2 
3 int main(void)
4 {
5    printf("Hello World!\n");      
6    return 0;  
7 }

  用匯編語言編制的程式就是完成某一特定任務的一組指令的集合。 而C語言程式是面向過程的語言 , 組成該程式的語句比彙編指令容易理解 , 因為它和人類所習慣的表達方式比較接近。 只是運算子號以及語法有所不同。但無論用什麼語言編制的程式, 都是為讓計算機完成某一特定功能而編寫的文字檔案。 這些文字檔案是不能直接在機器上執行的, 它們必須經過系統軟體(包括編輯器和編譯器)的輸入並編譯或彙編後,轉換成二進位制的可執行程式碼, 才是計算機可以識別的機器語言。 此時。程式就是一個包含二進位制可執行程式碼檔案的模組。 當核心把二進位制的可執行程式碼裝入記憶體後, 它由三部分組成:程式碼段、 資料段和堆疊段,線性地址的低地址欄位是程式碼段 , 存放程式經編譯後的可執行程式碼(程式文字)。 

  在作業系統中,程式碼段是隻讀的、不能修改,所以,程式碼段的長度是不會改變的。 在程式文字的上方是資料段,用來存放程式的變數 、字串和其他資料。它分為初始化靜態資料和未初始化靜態資料 (BSS)。資料段的長度是可以改變的。程式可以修改其中的變數。許多程式在執行時需要動態分配記憶體。在UNIX中可以使用系統呼叫 malloc ()函式來獲得。在資料區上方是堆疊段。 一般情況下, 堆疊段起始於虛擬地址空間的高階,棧向下方增長。當系統啟動程式執行時,堆疊中存放有環境變數和命令列引數。程式的可執行程式碼調人記憶體後,這三部分統稱為程式映像。 在虛擬地址空間 ,程式碼段 、資料段和堆疊段的地址空間是連續的。而在實體記憶體中,程式碼段、資料段和堆疊段的地址空間不一定是連續的。

  在記憶體中的程式映像執行之前,作業系統還要為其在核心的程序表項中分配一個被稱為程序控制塊的核心資料結構。同時要在該核心資料結構中加入程式執行時所需要的相應資訊,其中最為重要的資訊有:

• 指令指標:用以指示該程序正在執行的那條程式程式碼指令的地址。
• 程序的使用者識別符號。
• 程序的程式文字和資料區的儲存器位置。
• 程序的狀態。

  由此可以看出,程序映像使程式完全具備了在多道程式環境中執行的基本條件。因此,核心為程式映像分配了程序控制塊後 ,程式映像便轉化成為程序映像當排程程式排程該程序映像執行時,就是所謂的程序。程序控制塊是形成程序的最為重要的核心資料結構, 它是核心控制程序的資料結構。因為系統核心是根據程序控制塊中的資訊對正在執行的程式實施控制,以便完成程式所要求的操作可以用類比的方法來說明程式與程序之間的區別:
  程式是一個專案在行動之前的計劃書(或叫行動方案),而程序就是對該計劃書的實施過程。 顯然,計劃書和計劃書的實施過程存在著很大的差別。 因為,雖然計劃書是計劃實施過程中一個不可缺少的組成部分,但不是全部,在計劃書的實施過程中, 除了計劃書這個文字檔案(程式)外 。還需要人力資源和物力資源以及相應的資訊資源。比方說,蓋一幢大樓。 光有人樓的設計方案(圖紙)是不行的, 還必須要有施工辦公室(或稱為指揮控制部門,相當於CPU和作業系統)、負責調配人力資源和物力資源以及保證施工方案順利進行的各種措施(資訊資源)。 如果沒有人力資源 、物力資源以及執行機制去實施設計方案,那麼計劃書上的大樓將永遠是紙上的大樓。 但是計劃書的實施過程也永遠離不開計劃書。 另外,在計劃書實施過程的不同階段,工程可能處於不同的狀態,例如:由於等待建築材料或者是由於缺乏勞動力而小得不停工,此時工程將處於等待狀態;當各項條件具備時再繼續施工,此時工程處於執行態,下面給出程序的抽象定義:

  定義 2.1 (I)   程序是一個多元組,P = (p, h, f, e, s) ,其中: P 代表程序; p是可執行程式程式碼程式資料堆疊;h和f分別是執行程式程式碼時所需要的硬體軟體資源;硬體資源包括CPU特殊功能暫存器記憶體磁碟等;軟體資源包括核心資料結構以及相關的所有資訊資源; s代表程序執行期間的各種狀態;e是程式執行期間需要的控制資訊

  (2)  程序能夠動態產生、執行,也有生命週期,能夠被核心終止。

1.2.2  從併發機制理解程序

  以上是從程序的資料結構描述了程序與程式的區別。 程式映像變成程序映像的時刻也就意味著程式將被排程執行,這樣一個新的程序使產生了。下面從併發機制來理解程序和程式的區別,在一個併發處理系統中,當代碼裝入記憶體之後,核心允許多個或多個程序來執行它。也就是說, 允許多個程序併發執行同一程式碼段。 這就相當於,在同時啟動的全國各地的香格里拉大酒店的建築下地上,可以使用完全相同的酒店建築設計圖紙 ,這個建築設計圖紙就是可以共享的程式程式碼。
  如果多個程序併發執行同一程式碼段,那麼要允許各個程序按照其各自的情況在任何時間開始執行和結束執行。因此,在同一時刻, 毋個程序處在同一程式碼段的不同機器指令處執行(相當於幾個建築工地的進度不同)。為了使執行同一程式碼的多個程序之間的資料和進度相互獨立,必須為每個程序設定一個指令指標,指示該程序下一步將執行哪條指令。同時,每個程序要有自己的資料段和堆疊段用來儲存程式執行過程中用到的變數值。

  下面我們分析一個簡短的迴圈列印的C語言程式,就可以進一步得明白程式與程序的差別:

1 int i;
2 for(i = 5; i > 0; i--)
3 {
4   printf("Value of i is %d:\n",i);
5 }

停電了,後面繼續!!