1. 程式人生 > 實用技巧 >作業系統(三)

作業系統(三)

>>> hot3.png

3.1 什麼是程序

3.1.1 背景

在給程序下定義之前,先總結一下第1、2章介紹的一些概念:

  • 一個計算機平臺包括一組硬體資源,比如處理器、記憶體、I/O 模組、定時器和磁碟驅動器等。
  • 計算機程式是為執行某些任務而開發的。在典型的情況下,它們接受外來的輸入,做一些處理之後,輸出結果。
  • 直接根據給定的硬體平臺寫應用程式效率是低下的,主要原因如下:
    • 針對相同的平臺可以開發出很多應用程式,所以開發出這些應用程式訪問計算機資源的通用例程是很有意義的。
    • 處理器本身只能對多道程式設計提供有限的支援,需要用軟體去管理處理器和其他資源同時被多個程式共享。
    • 如果多個程式在同一時間都是活躍的,那麼需要保護每個程式的資料、I/O 使用和其他資源不被其他程式佔用。
  • 開發作業系統是為了給應用程式提供一個方便、安全和一致的介面。作業系統是計算機硬體和應用程式之間的一層軟體,對應用程式和工具提供了支援。
  • 可以把作業系統想象為資源的統一抽象表示,可以被應用程式請求和訪問。資源包括記憶體、網路介面和檔案系統等。一旦作業系統為應用程式建立了這些資源的抽象表示,就必須管理它們的使用,例如一個作業系統可以允許資源共享和資源保護。

有了應用程式、系統軟體和資源的概念,就可以討論作業系統怎樣以一個有序的方式管理應用程式的執行,以達到以下目的:

  • 資源對多個應用程式是可用的。
  • 物理處理器在多個應用程式間切換以保證所有程式都在執行中。
  • 處理器和I/O 裝置能得到充分的利用。

所有現代作業系統採用的方法都是依據對應於一個或多個程序存在的應用程式執行的一種模型。

3.1.2 程序和程序控制塊

第2章給程序下了以下幾個定義:

  • 正在執行的程式。
  • 正在計算機上執行的程式例項。
  • 能分配給處理器並由處理器執行的實體。
  • 具有以下特徵的活動單元:一組指令序列的執行、一個當前狀態和相關的系統資源集。

也可以把程序當成由一組元素組成的實體,程序的兩個基本的元素是程式程式碼(可能被執行相同程式的其他程序共享)和程式碼相關聯的資料集。
假設處理器開始執行這個程式程式碼,且把這個執行實體叫做程序。
在程序執行時,任意給定一個時間,程序都可以唯一地被表徵為以下元素:

  • 識別符號:跟這個程序相關的唯一識別符號,用來區別其他程序。
  • 狀態:如果程序正在執行,那麼程序處於執行態。
  • 優先順序:相對於其他程序的優先順序。
  • 程式計數器:程式中即將被執行的下一條指令的地址。
  • 記憶體指標:包括程式程式碼和程序相關資料的指標,還有和其他程序共享記憶體塊的指標。
  • 上下文資料:程序執行時處理器的暫存器中的資料。
  • I/O 狀態資訊:包括顯式的I/O 請求、分配給程序的I/O 裝置(例如磁帶驅動器)和被程序使用的檔案列表等。
  • 記賬資訊:可能包括處理器時間總和、使用的時鐘數總和、時間限制、記賬號等。

上述列表資訊存放在一個叫做程序控制塊(如圖)的資料結構中,該控制塊由作業系統建立和管理。程序控制塊包含了充分的資訊,這樣就可以中斷一個程序的執行,並且在後來恢復執行程序時就好像程序未被中斷過。程序控制塊是作業系統能夠支援多程序和提供多處理的關鍵工具。當程序被中斷時,作業系統會把程式計數器和處理器暫存器(上下文資料)儲存到程序控制塊中的相應位置,程序狀態也被改變為其他的值,例如阻塞態或就緒態(後面將講述)。作業系統可以把其他程序設定為執行態,把其他程序的程式計數器和程序上下文資料載入到處理器暫存器中,這樣其他程序就可以開始執行了。

圖3.1 簡化的程序控制塊

因此,程序是由程式程式碼和相關資料還有程序控制塊組成。對於一個單處理器計算機,在任何時間都最多隻有一個程序在執行,正在執行的這個程序的狀態為執行態。

3.2 程序狀態

正如前面所提到的,對一個被執行的程式,作業系統會為該程式建立一個程序或任務。

  • 從處理器的角度看,它在指令序列中按某種順序執行指令,這個順序根據程式計數器暫存器中不斷變化的值來指示,程式計數器可能指向不同程序中不同部分的程式程式碼;
  • 從程式自身的角度看,它的執行涉及程式中的一系列指令。可以通過列出為該程序執行的指令序列來描述單個程序的行為,這樣的序列稱做程序的軌跡。可以通過給出各個程序的軌跡是如何被交替的來描述處理器的行為。

舉例,圖3.2 給出了三個程序在記憶體中的佈局,為簡化討論,假設沒有使用虛擬記憶體,因此所有三個程序都由完全載入記憶體中的程式表示,此外,有一個小的分派器一1使處理器從一個程序切換到另一個程序。圖3.3 給出了這三個程序在執行過程早期的軌跡,給出了程序A 和C 中最初執行的12 條指令,程序B 執行4 條指令,假設第4 條指令呼叫了程序必須等待的I/O 操作。

圖3.2 在指令週期13 時的執
行快照(如圖3.4 所示)

現在從處理器的角度看這些軌跡。圖3.4 給出了最初的52個指令週期中交替的軌跡(為方便起見,指令週期都給出了編號)。在圖中,陰影部分代表由分配器執行的程式碼。在每個例項中由分派器執行的指令順序是相同的,因為是分派器的同一個功能在執行。假設作業系統僅允許一個程序最多連續執行6 個指令週期,在此之後將被中斷,這避免了任何一個程序獨佔處理器時間。如圖3.4 所示,程序A 最初的6 條指令被執行,接下來是一個超時並執行分派器的某些程式碼,在控制轉移給程序B 之前分派器執行了6 條指令二2。在程序B 的4 條指令被執行後,程序B 請求一個它必須等待的I/O 動作,因此,處理器停止執行程序B,並通過分派器轉移到程序C。在超時後,處理器返回程序A,當這次處理超時時,程序B 仍然等待那個I/O 操作的完成,因此分派器再次轉移到程序C。

(點選檢視大圖)圖3.3 圖3.2 中程序的軌跡
(點選檢視大圖)圖3.4 圖3.2 中程序的組合軌跡

3.2.1 兩狀態程序模型

作業系統的基本職責是控制程序的執行,這包括確定交替執行的方式和給程序分配資源。在設計控制程序的程式時,第一步就是描述程序所表現出的行為。

任何時刻,一個程序可以處於以下兩種狀態之一:執行態或未執行態,如圖3.5a 所示。當作業系統建立一個新程序時,它將該程序以未執行態加入到系統中,作業系統知道這個程序是存在的,並正在等待執行機會。當前正在執行的程序不時地被中斷,作業系統中的分派器部分將選擇一個新程序執行。前一個程序從執行態轉換到未執行態,另外一個程序轉換到執行態。

從這個簡單的模型可以意識到作業系統的一些設計元素。必須用某種方式來表示每個程序,使得作業系統能夠跟蹤它,也就是說,必須有一些與程序相關的資訊,包括程序在記憶體中的當前狀態和位置,即程序控制塊。未執行的程序必須保持在某種型別的佇列中,並等待它們的執行時機。圖3.5b 給出了一個結構,結構中有一個佇列,佇列中的每一項都指向某個特定程序的指標,或佇列可以由資料塊構成的連結串列組成,每個資料塊表示一個程序。

可以用該排隊圖描述分派器的行為。被中斷的程序轉移到等待程序佇列中,或者,如果程序已經結束或取消,則被銷燬(離開系統)。在任何一種情況下,分派器均從佇列中選擇一個程序來執行。

圖3.5 兩狀態程序模型

3.2.2 程序的建立和終止

在對簡單的兩狀態模型進行改進之前,討論一下程序的建立和終止。無論使用哪種程序行為模型,程序的生存期都圍繞著程序的建立和終止。

程序的建立

當一個新程序新增到那些正在被管理的程序集合中去時,作業系統需要建立用於管理該程序的資料結構(見3.3 節),並在記憶體中給它分配地址空間。將在3.3 節中講述這些資料結構,這些行為構成了一個新程序的建立過程。

通常有4 個事件會導致建立一個程序,如表。

  • 在批處理環境中,響應作業提交時會建立程序;
  • 在互動環境中,當一個新使用者試圖登入時會建立程序。

不論在哪種情況下,作業系統都負責新程序的建立;作業系統也可能會代表應用程式建立程序,eg:如果使用者請求列印一個檔案,則作業系統可以建立一個管理列印的程序,進而使請求程序可以繼續執行,與完成列印任務的時間無關。

表3.1 導致程序建立的原因

傳統地,作業系統建立程序的方式對使用者和應用程式都是透明的。但允許一個程序引發另一個程序的建立將是很有用的。例如,一個應用程式程序可以產生另一個程序,以接收應用程式產生的資料,並將資料組織成適合以後分析的格式。新程序與應用程式並行地執行,並當得到新的資料時被啟用。這個方案對構造應用程式是非常有用的,例如,伺服器程序(如列印伺服器、檔案伺服器)可以為它處理的每個請求產生一個新程序。當作業系統為另一個程序的顯式請求建立一個程序時,這個動作稱為程序派生

當一個程序派生另一個程序時,前一個稱做父程序,被派生的程序稱做子程序。在典型的情況下,相關程序需要相互之間的通訊和合作。相關主題將在第5 章講述。

程序終止

表3.2 概括了程序終止的典型原因。任何一個計算機系統都必須為程序提供表示其完成的方法,批處理作業中應該包含一個Halt 指令或用於終止的作業系統顯式服務呼叫來終止。

  • Halt 指令將產生一箇中斷,警告作業系統一個程序已經完成。
  • 對互動式應用程式,使用者的行為將指出何時程序完成,例如,在分時系統中,當用戶退出系統或關閉自己的終端時,該使用者的程序將被終止。在個人計算機或工作站中,使用者可以結束一個應用程式(如字處理或電子表格)。所有這些行為最終導致傳送給作業系統的一個服務請求,以終止發出請求的程序。
  • 此外,很多錯誤和故障條件會導致程序終止。表3.2 列出了一些最常見的識別條件。
  • 最後,在有些作業系統中,程序可以被建立它的程序終止,或當父程序終止時而終止。

表3.2 導致程序終止的原因

3.2.3 五狀態模型

如果所有程序都做好了執行準備,則圖3.5b 所給出的排隊原則是有效的。佇列是“先進先出”的表,對於可執行的程序處理器以一種輪轉方式操作(依次給佇列中的每個程序一定的執行時間,然後程序返回佇列,阻塞情況除外)。但即使對前面描述的簡單例子,這個實現都是不合適的:存在著一些處於非執行狀態但已經就緒等待執行的程序,而同時存在另外的一些處於阻塞狀態等待I/O 操作結束的程序。因此,如果使用單個佇列,分派器不能只考慮選擇佇列中最老的程序,相反,它應該掃描這個列表,查詢那些未被阻塞且在佇列中時間最長的程序

解決這種情況的一種比較自然的方法是將非執行狀態分成兩個狀態:就緒(ready)和阻塞(blocked),如下圖。新圖中的5 個狀態如下:

圖3.6 五狀態程序模型
  • 執行態:該程序正在執行。在本章中,假設計算機只有一個處理器,因此一次最多隻有一個程序處於這個狀態。
  • 就緒態:程序做好了準備,只要有機會就開始執行。
  • 阻塞/等待態:程序在某些事件發生前不能執行,如I/O 操作完成。
  • 新建態:剛剛建立的程序,作業系統還沒有把它加入到可執行程序組中。通常是程序控制塊已經建立但還沒有載入到記憶體中的新程序。
  • 退出態:作業系統從可執行程序組中釋放出的程序,或者是因為它自身停止了,或者是因為某種原因被取消。

新建狀態對應於剛剛定義的程序。例如,如果一位新使用者試圖登入到分時系統中,或者一個新的批作業被提交執行,那麼作業系統可以分兩步定義新程序。

  • 首先,作業系統執行一些必需的輔助工作,將識別符號關聯到程序,分配和建立管理程序所需要的所有表。此時,程序處於新建狀態,這意味著作業系統已經執行了建立程序的必需動作,但還沒有執行程序。例如,作業系統可能基於效能或記憶體侷限性的原因,限制系統中的程序數量。當程序處於新建態時,作業系統所需要的關於該程序的資訊儲存在記憶體中的程序表中,但程序自身還未進入記憶體,就是即將執行的程式程式碼不在記憶體中,也沒有為與這個程式相關的資料分配空間。當程序處於新建態時,程式保留在外存中,通常是磁碟中。

類似地,程序退出系統也分為兩步。

  • 首先,當程序到達一個自然結束點時,由於出現不可恢復的錯誤而取消時,或當具有相應許可權的另一個程序取消該程序時,程序被終止;終止使程序轉換到退出態,此時,程序不再被執行了,與作業相關的表和其他資訊臨時被作業系統保留起來,這給輔助程式或支援程式提供了提取所需資訊的時間。一個實用程式為了分析效能和利用率,可能需要提取程序的歷史資訊,一旦這些程式都提取了所需要的資訊,作業系統就不再需要保留任何與該程序相關的資料,該程序將從系統中刪除。

圖3.6 顯示了導致程序狀態轉換的事件型別。可能的轉換如下:

  • 空→新建:建立執行一個程式的新程序。這個事件在表3.1 中所列出的原因下都會發生。
  • 新建→就緒:作業系統準備好再接納一個程序時,把一個程序從新建態轉換到就緒態。大多數系統基於現有的程序數或分配給現有程序的虛擬記憶體數量設定一些限制,以確保不會因為活躍程序的數量過多而導致系統的效能下降。
  • 就緒→執行:需要選擇一個新程序執行時,作業系統選擇一個處於就緒態的程序,這是排程器或分派器的工作。程序的選擇問題將在第四部分探討。
  • 執行→退出:如果當前正在執行的程序表示自己已經完成或取消,則它將被作業系統終止,見表3.2。
  • 執行→就緒:這類轉換最常見的原因是,正在執行的程序到達了“允許不中斷執行”的最大時間段;實際上所有多道程式作業系統都實行了這類時間限定。這類轉換還有很多其他原因,例如作業系統給不同的程序分配不同的優先順序,但這不是在所有的作業系統中都實現了的。假設,程序A 在一個給定的優先順序執行,且具有更高優先順序的程序B 正處於阻塞態。如果作業系統知道程序B 等待的事件已經發生了,則將B 轉換到就緒態,然後因為優先順序的原因中斷程序A 的執行,將處理器分派給程序B,我們說作業系統搶佔了程序A。最後一種情況是,程序自願釋放對處理器的控制,例如一個週期性地進行記賬和維護的後臺程序。
  • 執行→阻塞:如果程序請求它必須等待的某些事件,則進入阻塞態。對作業系統的請求通常以系統服務呼叫的形式發出,也就是說,正在執行的程式請求呼叫作業系統中一部分程式碼所發生的過程。例如,程序可能請求作業系統的一個服務,但作業系統無法立即予以服務,它也可能請求了一個無法立即得到的資源,如檔案或虛擬記憶體中的共享區域;或者也可能需要進行某種初始化的工作,如I/O 操作所遇到的情況,並且只有在該初始化動作完成後才能繼續執行。當程序互相通訊,一個程序等待另一個程序提供輸入時,或者等待來自另一個程序的資訊時,都可能被阻塞。
  • 阻塞→就緒:當所等待的事件發生時,處於阻塞態的程序轉換到就緒態。
  • 就緒→退出:為了清楚起見,狀態圖中沒有表示這種轉換。在某些系統中,父程序可以在任何時刻終止一個子程序。如果一個父程序終止,與該父程序相關的所有子程序都將被終止。
  • 阻塞→退出:前面一項提供了註釋。

再回到前面的簡單例子,圖3.7 顯示了每個程序在狀態間的轉換,圖3.8a 給出了可能實現的排隊規則,有兩個佇列:就緒佇列和阻塞佇列。進入系統的每個程序被放置在就緒佇列中,當作業系統選擇另一個程序執行時,將從就緒佇列中選擇。對於沒有優先順序的方案,這可以是一個簡單的先進先出佇列。當一個正在執行的程序被移出處理器時,它根據情況或者被終止,或者被放置在就緒或阻塞佇列中。最後,當一個事件發生時,所有位於阻塞佇列中等待這個事件的程序都被轉換到就緒佇列中。

圖3.7 圖3.4 中的程序狀態

後一種方案意味著當一個事件發生時,作業系統必須掃描整個阻塞佇列,搜尋那些等待該事件的程序。在大型作業系統中,佇列中可能有幾百甚至幾千個程序,因此,擁有多個佇列將會很有效,一個事件可以對應一個佇列。那麼,當事件發生時,相應佇列中的所有程序都轉換到就緒態(見圖3.8b)。

(點選檢視大圖)圖3.8 圖3.6 的排隊模型

最後還有一種改進是,如果按照優先順序方案分派程序,維護多個就緒佇列(每個優先順序一個佇列)將會帶來很多的便利。作業系統可以很容易地確定哪個就緒程序具有最高的優先順序且等待時間最長。

3.2.4 被掛起的程序

交換的需要

前面描述的三個基本狀態(就緒態、執行態和阻塞態)提供了一種為程序行為建立模型的系統方法,但可以證明往模型中增加其他狀態也是合理的。為了說明加入新狀態的好處,考慮一個沒有使用虛擬記憶體的系統,每個被執行的程序必須完全載入記憶體,因此,圖3.8b 中,所有佇列中的所有程序必須駐留在記憶體中。

所有這些設計機制的原因都是由於I/O 活動比計算速度慢很多,因此在單道程式系統中的處理器在大多數時候是空閒的。但是圖3.8b 的方案並沒有完全解決這個問題。在這種情況下,記憶體儲存有多個程序,當一個程序正在等待時,處理器可以轉移到另一個程序,但是處理器比I/O要快得多,以至於記憶體中所有的程序都在等待I/O 的情況很常見。因此,即使是多道程式設計,大多數時候處理器仍然可能處於空閒狀態。

一種解決方法是記憶體可以被擴充以適應更多的程序,但是這種方法有兩個缺陷。

  • 首先是記憶體的價格問題,當記憶體大小增加到兆位及千兆位時,價格也會隨之增加;
  • 再者,程式對記憶體空間需求的增長速度比記憶體價格下降的速度快。因此,更大的記憶體往往導致更大的程序,而不是更多的程序。

另一種解決方案是交換,包括把記憶體中某個程序的一部分或全部移到磁碟中。當記憶體中沒有處於就緒狀態的程序時,作業系統就把被阻塞的程序換出到磁碟中的“掛起佇列”。作業系統在此之後取出掛起佇列中的另一個程序,或者接受一個新程序的請求,將其納入記憶體執行。程序行為模型(見圖3.9a)中增加另一個狀態:掛起態。

“交換”是一個I/O 操作,因而也可能使問題更加惡化。但是由於磁碟I/O 一般是系統中最快的I/O(相對於磁帶或印表機I/O),所以交換通常會提高效能。

當作業系統已經執行了一個換出操作,它可以有兩種將一個程序取到記憶體中的選擇:

  • 可以接納一個新近建立的程序;
  • 或調入一個以前被掛起的程序。

通常比較傾向於調入一個以前被掛起的程序,給它提供服務,而不是增加系統中的負載總數。

但所有已經掛起的程序在掛起時都處於阻塞態。顯然,這時把被阻塞的程序取回記憶體沒有任何意義,因為它仍然沒有準備好執行。但是,掛起狀態中的每個程序最初是阻塞在一個特定的事件上,當這個事件發生時,程序就不再阻塞,可以繼續執行。因此,需要重新考慮設計方式。有兩個獨立的概念:程序是否在等待一個事件(阻塞與否)以及程序是否已經被換出記憶體(掛起與否)。為適應這種2×2 的組合,需要4 個狀態:

  • 就緒態:程序在記憶體中並可以執行。
  • 阻塞態:程序在記憶體中並等待一個事件。
  • 阻塞/掛起態:程序在外存中並等待一個事件。
  • 就緒/掛起態:程序在外存中,但是隻要被載入記憶體就可以執行。
圖3.9 有掛起態的程序狀態轉換圖

在檢視包含兩個新掛起狀態的狀態轉換圖之前,必須提到另一點。到現在為止的論述都假設沒有使用虛擬記憶體,程序或者都在記憶體中,或者都在記憶體之外。在虛擬記憶體方案中,可能會執行到只有一部分內容在記憶體中的程序,如果訪問的程序地址不在記憶體中,則程序的相應部分可以被調入記憶體。虛擬記憶體的使用看上去會消除顯式交換的需要,這是因為通過處理器中的儲存管理硬體,任何期望的程序中的任何期望的地址都可以移入或移出記憶體。但是,正如在第8 章中將會看到的,如果有足夠多的活動程序,並且所有程序都有一部分在記憶體中,則有可能導致虛擬記憶體系統崩潰。因此,即使在虛擬儲存系統中,作業系統也需要不時地根據執行情況顯式地、完全地換出程序。

現在來看圖3.9b 中我們已開發的狀態轉換模型(圖中的虛線表示可能但並不是必需的轉換)。比較重要的新的轉換如下:

  • 阻塞→阻塞/掛起:如果沒有就緒程序,則至少一個阻塞程序被換出,為另一個沒有阻塞的程序讓出空間。如果作業系統確定當前正在執行的程序,或就緒程序為了維護基本的效能要求而需要更多的記憶體空間,那麼,即使有可用的就緒態程序也可能出現這種轉換。
  • 阻塞/掛起→就緒/掛起:如果等待的事件發生了,則處於阻塞/掛起狀態的程序可以轉換到就緒/掛起狀態。注意,這要求作業系統必須能夠得到掛起程序的狀態資訊。
  • 就緒/掛起→就緒:如果記憶體中沒有就緒態程序,作業系統需要調入一個程序繼續執行。此外,當處於就緒/掛起態的程序比處於就緒態的任何程序的優先順序都要高時,也可以進行這種轉換。這種情況的產生是由於作業系統設計者規定調入高優先順序的程序比減少交換量更重要。
  • 就緒→就緒/掛起:通常,作業系統更傾向於掛起阻塞態程序而不是就緒態程序,因為就緒態程序可以立即執行,而阻塞態程序佔用了記憶體空間但不能執行。但如果釋放記憶體以得到足夠空間的唯一方法是掛起一個就緒態程序,那麼這種轉換也是必需的。並且,如果作業系統確信高優先順序的阻塞態程序很快將會就緒,那麼它可能選擇掛起一個低優先順序的就緒態程序,而不是一個高優先順序的阻塞態程序。

還需要考慮的幾種其他轉換有:

  • 新建→就緒/掛起以及新建→就緒:當建立一個新程序時,該程序或者加入到就緒佇列,或者加入到就緒/掛起佇列中。不論哪種情況,作業系統都必須建立一些表以管理程序,併為程序分配地址空間。作業系統可能更傾向於在初期執行這些輔助工作,這使得它可以維護大量的未阻塞的程序。通過這個策略,記憶體中經常會沒有足夠的空間分配給新程序,因此使用了(新建→就緒/掛起)轉換。另一方面,我們可以證明建立程序的適時(just-in-time)原理,即儘可能推遲建立程序以減少作業系統的開銷,並在系統被阻塞態程序阻塞時允許作業系統執行程序建立任務。
  • 阻塞/掛起→阻塞:這種轉換在設計中比較少見,如果一個程序沒有準備好執行,並且不在記憶體中,調入它又有什麼意義?但是考慮到下面的情況:一個程序終止,釋放了一些記憶體空間,阻塞/掛起佇列中有一個程序比就緒/掛起佇列中的任何程序的優先順序都要高,並且作業系統有理由相信阻塞程序的事件很快就會發生,這時,把阻塞程序而不是就緒程序調入記憶體是合理的。
  • 執行→就緒/掛起:通常當分配給一個執行程序的時間期滿時,它將轉換到就緒態。但是,如果由於位於阻塞/掛起佇列的具有較高優先順序的程序變得不再被阻塞,作業系統搶佔這個程序,也可以直接把這個執行程序轉換到就緒/掛起隊形中,並釋放一些記憶體空間。
  • 各種狀態→退出:在典型情況下,一個程序在執行時終止,或者是因為它已經完成,或者是因為出現了一些錯誤條件。但是,在某些作業系統中,一個程序可以被建立它的程序終止,或當父程序終止時終止。如果允許這樣,則程序在任何狀態時都可以轉換到退出態。

掛起的其他用途

到目前為止,掛起程序的概念與不在記憶體中的程序概念是等價的一個不在記憶體中的程序,不論它是否在等待一個事件,都不能立即執行。

我們可以總結一下掛起程序的概念。首先,按照以下特點定義掛起程序:

  1. 程序不能立即執行。
  2. 程序可能是或不是正在等待一個事件。如果是,阻塞條件不依賴於掛起條件,阻塞事件的發生不會使程序立即被執行。
  3. 為阻止程序執行,可以通過代理把這個程序置於掛起狀態,代理可以是程序自己,也可以是父程序或作業系統。
  4. 除非代理顯式地命令系統進行狀態轉換,否則程序無法從這個狀態中轉移。

表3.3 列出了程序的一些掛起原因。

  • 提供更多的記憶體空間,這樣可以調入一個就緒/掛起態程序或增加分配給其他就緒態程序的記憶體;
  • 作業系統因為其他動機而掛起一個程序,例如,記賬或跟蹤程序可能用於監視系統的活動,可以使用程序記錄各種資源(處理器、記憶體、通道)的使用情況以及系統中使用者程序的進行速度。在操作員控制下的作業系統可以不時地開啟或關閉這個程序。
  • 如果作業系統發現或懷疑有問題,它可以掛起程序。死鎖就是一個例子,將在第6 章講述。
  • 另一個例子是,如果在程序測試時檢測到通訊線路中的問題,操作員讓作業系統掛起使用該線路的程序。

另外一些原因關係到互動使用者的行為。例如,如果使用者懷疑程式中有缺陷,他(她)可以掛起執行程式並進行除錯,檢查並修改程式或資料,然後恢復執行;或者可能有一個收集記錄或記賬的後臺程式,使用者可能希望能夠開啟或關閉這個程式。

表3.3 導致程序掛起的原因

時機的選擇也會導致一個交換決策。例如,如果一個程序週期性地被啟用,但大多數時間是空閒的,則在它在兩次使用之間應該被換出。監視使用情況或使用者活動的程式就是一個例子。

最後,父程序可能會希望掛起一個後代程序。例如,程序A 可以生成程序B,以執行檔案讀操作;隨後,程序B 在讀檔案的過程中遇到錯誤,並報告給程序A;程序A 掛起程序B,調查錯誤的原因。

在所有這些情況中,掛起程序的活動都是由最初請求掛起的代理請求的。

3.3 程序描述

作業系統控制計算機系統內部的事件,它為處理器執行程序而進行排程和分派,給程序分配資源,並響應使用者程式的基本服務請求。因此,我們可以把作業系統看做是管理系統資源的實體。

這個概念如圖3.10 所示。在多道程式設計環境中,在虛擬記憶體中有許多已經建立了的程序,每個程序在執行期間,需要訪問某些系統資源,包括處理器、I/O 裝置和記憶體。在圖中,程序P1 正在執行,該程序至少有一部分在記憶體中,並且還控制著兩個I/O 裝置;程序P2 也在記憶體中,但由於正在等待分配給P1 的I/O 裝置而被阻塞;程序Pn 已經被換出,因此是掛起的。

以後幾章中將探討作業系統代表程序管理這些資源的細節。這裡關心的是一些最基本的問題:作業系統為了控制程序和管理資源需要哪些資訊?

圖3.10 程序和資源(某一時刻的資源分配)

3.3.1 作業系統的控制結構

作業系統為了管理程序和資源,必須掌握關於每個程序和資源當前狀態的資訊。普遍使用的方法是:作業系統構造並維護它所管理的每個實體的資訊表。如圖3.11,作業系統維護著4 種不同型別的表:記憶體、I/O、檔案和程序

圖3.11 作業系統控制表的通用結構

一、記憶體表用於跟蹤內(實)存和外存(虛擬記憶體)。記憶體的某些部分為作業系統而保留,剩餘部分是程序可以使用的,儲存在外存中的程序使用某種型別的虛擬記憶體或簡單的交換機制。記憶體表必須包括以下資訊:

  • 分配給程序的記憶體。
  • 分配給程序的外存。
  • 記憶體塊或虛擬記憶體塊的任何保護屬性,如哪些程序可以訪問某些共享記憶體區域。
  • 管理虛擬記憶體所需要的任何資訊。

第三部分將詳細講述用於記憶體管理的資訊結構。

二、作業系統使用I/O 表管理計算機系統中的I/O 裝置和通道。在任何給定的時刻,一個I/O 裝置或者是可用的,或者已分配給某個特定的程序,如果正在進行I/O 操作,則作業系統需要知道I/O 操作的狀態和作為I/O 傳送的源和目標的記憶體單元。在第11 章將詳細講述I/O 管理。

三、作業系統維護著檔案表,這些表提供關於檔案是否存在、檔案在外存中的位置、當前狀態和其他屬性的資訊。大部分資訊(不是全部資訊)可能由檔案管理系統維護和使用。在這種情況下,作業系統只有一點或者沒有關於檔案的資訊;在其他作業系統中,很多檔案管理的細節由作業系統自己管理。這方面的內容將在第12 章講述。

四、最後,作業系統為了管理程序必須維護程序表。需要先明確兩點:首先,儘管圖3.11 給出了4 種不同的表,但是這些表必須以某種方式連結起來或交叉引用。記憶體、I/O 和檔案是代表程序而被管理的,因此程序表中必須有對這些資源的直接或間接引用。檔案表中的檔案可以通過I/O 裝置訪問,有時它們也位於記憶體中或虛擬記憶體中。這些表自身必須可以被作業系統訪問到,因此它們受制於記憶體管理。

其次,作業系統最初如何知道建立表?顯然,作業系統必須有基本環境的一些資訊,如有多少記憶體空間、I/O 裝置是什麼以及它們的識別符號是什麼等。這是一個配置問題,也就是說,當作業系統初始化後,它必須可以使用定義基本環境的某些配置資料,這些資料必須在作業系統之外,通過人的幫助或一些自動配置軟體而產生。

3.3.2 程序控制結構

作業系統在管理和控制程序時,首先必須知道程序的位置以及在管理時所必需的程序屬性(如程序ID、程序狀態)。

程序位置

程序的物理表示是什麼?

  • 程序最少必須包括一個或一組被執行的程式,與這些程式相關聯的是區域性變數、全域性變數和任何已定義常量的資料單元。因此,一個程序至少包括足夠的記憶體空間,以儲存該程序的程式和資料;
  • 此外,程式的執行通常涉及用於跟蹤過程呼叫和過程間引數傳遞的(見附錄1B)。
  • 最後,與每個程序相關聯的還有作業系統用於控制程序的許多屬性,通常,屬性的集合稱做程序控制塊。

程式、資料、棧和屬性的集合稱做程序映像(process image)(見表3.4程序映像中的典型元素)。

程序映像的位置依賴於使用的記憶體管理方案。
最簡單的情況,程序映像儲存在鄰近的或連續的儲存塊中。該儲存塊位於外存(通常是磁碟)中。如果作業系統要管理程序,其程序映像至少有一部分必須位於記憶體中,為執行該程序,整個程序映像必須載入記憶體中或至少載入虛擬記憶體中。因此,作業系統需要知道每個程序在磁碟中的位置,並且對於記憶體中的每個程序,需要知道其在記憶體中的位置。來看一下第2 章中CTSS 作業系統關於這個方案的一個稍微複雜些的變體。在CTSS 中,當程序被換出時,部分程序映像可能保留在記憶體中。因此,作業系統必須跟蹤每個程序映像的哪一部分仍然在記憶體中。

現代作業系統假定分頁硬體允許用不連續的實體記憶體來支援部分常駐記憶體的程序。在任何給定的時刻,程序映像的一部分可以在記憶體中,剩餘部分可以在外存中。因此,作業系統維護的程序表必須表明每個程序映像中每頁的位置。

圖3.11 描繪了位置資訊的結構。有一個主程序表,每個程序在表中都有一個表項,每一項至少包含一個指向程序映像的指標。如果程序映像包括多個塊,則這個資訊直接包含在主程序表中,或可以通過交叉引用記憶體表中的項得到。當然,這個描述是一般性描述,特定的作業系統將按自己的方式組織位置資訊。

程序屬性

每個程序的大量資訊該資訊可以保留在程序控制塊中。不同的系統以不同的方式組織該資訊。表3.5( 程序控制塊中的典型元素) 列出了每個程序資訊的簡單分類。

可以把程序控制塊資訊分成三類:

  • 程序標識資訊
  • 處理器狀態資訊
  • 程序控制資訊

一、每個程序都分配了一個唯一的數字識別符號。程序識別符號可以簡單地表示為主程序表(圖3.11 所示)中的一個索引;否則,必須有一個對映,使得作業系統可以根據程序識別符號定位相應的表。作業系統控制的許多其他表可以使用程序識別符號交叉引用程序表。例如,記憶體表可以組織起來以便提供一個關於記憶體的對映,指明每個區域分配給了哪個程序。I/O 表和檔案表中也會有類似的引用。當程序相互之間進行通訊時,程序識別符號可用於通知作業系統某一特定通訊的目標;當允許程序建立其他程序時,識別符號可用於指明每個程序的父程序和後代程序。

除了程序識別符號,還給程序分配了一個使用者識別符號,用於標明擁有該程序的使用者。

二、處理器狀態資訊包括處理器暫存器的內容。當一個程序正在執行時,其資訊當然在暫存器中。當程序被中斷時,所有的暫存器資訊必須儲存起來,使得程序恢復執行時這些資訊都可以被恢復。所涉及的暫存器的種類和數目取決於處理器的設計。在典型情況下,暫存器組包括使用者可見暫存器、控制和狀態暫存器和棧指標,這些在第1 章中都曾介紹過。

處理器都包括一個或一組通常稱做程式狀態字( PSW)的暫存器,它包含狀態資訊。PSW 通常包含條件碼和其他狀態資訊。Pentium處理器中的處理器狀態字就是一個很好的例子,它稱做EFLAGS 暫存器(如圖3.12 和表3.6 所示),可被執行在Pentium 處理器上的任何作業系統(包括UNIX 和Windows)使用。

三、程序控制資訊是作業系統控制和協調各種活動程序所需要的額外資訊。表3.5 表明了這類資訊的範圍,隨後章節將進一步詳細分析。

圖3.13 給出了虛擬記憶體中程序映像的結構。每個程序映像包括一個程序控制塊、使用者棧、程序的專用地址空間以及與別的程序共享的任何其他地址空間。在這個圖中,每個程序映像表現為一段地址相鄰的區域。在實際的實現中可能不是這種情況,這取決於記憶體管理方案和作業系統組織控制結構的方法。

圖3.12 PentiumⅡEFLAGS 暫存器

表3.6 Pentium EFLAGS 暫存器位

正如表3.5 中所指出的,程序控制塊還可能包含構造資訊,包括將程序控制塊連結起來的指標。因此,前一節中所描述的佇列可以由程序控制塊的連結串列實現,例如,圖3.8a 中的排隊結構可以按圖3.14 中的方式實現。

程序控制塊的作用

每個程序控制塊包含作業系統所需要的關於程序的所有資訊。實際上,作業系統中的每個模組,包括那些涉及排程、資源分配、中斷處理、效能監控和分析的模組,都可能讀取和修改它們。可以說,資源控制塊集合定義了作業系統的狀態。

(點選檢視大圖)圖3.13 虛擬記憶體中的使用者程序

作業系統中的很多例程都需要訪問程序控制塊中,直接訪問這些表並不難,每個程序都有一個唯一ID 號,可用作程序控制塊指標表的索引。困難的不是訪問而是保護,具體表現為下面兩個問題:

  • 一個例程(如中斷處理程式)中有錯誤,可能會破壞程序控制塊,進而破壞了系統對受影響程序的管理能力。
  • 程序控制塊的結構或語義的設計變化可能會影響到作業系統中的許多模組。

這些問題可以通過要求作業系統中的所有例程都通過一個處理例程來專門處理,處理例程的任務僅僅是保護程序控制塊,它是讀寫這些塊的唯一的仲裁程式。使用這類程序,需要權衡效能問題和對系統軟體剩餘部分正確性的信任程度。

圖3.14 程序連結串列結構

3.4 程序控制

3.4.1 執行模式

首先需區分“與作業系統相關聯的”以及“與使用者程式相關聯”的處理器執行模式。
某些指令只能在特權態下執行,包括讀取或改變諸如程式狀態字之類控制暫存器的指令、原始I/O 指令和與記憶體管理相關的指令。另外,有部分記憶體區域僅在特權態下可以被訪問到。

  • 非特權態常稱做使用者態,使用者程式通常在該模式下執行;
  • 特權態可稱做系統態、控制態或核心態,核心態指的是作業系統的核心,是作業系統中包含重要系統功能的部分。

表3.7 作業系統核心的典型功能

使用兩種模式的原因:保護作業系統和重要的作業系統表(如程序控制塊)不受使用者程式的干涉。在核心態下,軟體具有對處理器以及所有指令、暫存器和記憶體的控制能力,這一級的控制對使用者程式不是必需的,且為了安全起見也不是使用者程式可訪問的。

這樣產生了兩個問題:處理器如何知道它正在什麼模式下執行以及如何改變這一模式。
對第一個問題,程式狀態字中有一位表示執行模式,這一位應某些事件的要求而改變。在典型情況下,當用戶呼叫一個作業系統服務或中斷觸發系統例程的執行時,執行模式被設定成核心態,當從系統服務返回到使用者程序時,執行模式被設定為使用者態。例如64 位IA-64 體系結構的Intel Itanium 處理器,有一個處理器狀態暫存器(PSR),包含2 位的CPL(當前特權級別)域,級別0 是最高特權級別,級別3 是最低特權級別。大多數作業系統,如Linux,使用級別0 作為核心態,使用另一個級別作為使用者態。當中斷髮生時,處理器清空大部分PSR 中的位,包括CPL域,這將自動把CPL 設定為0。在中斷處理例程結束時,最後的一個指令是irt(中斷返回),這條指令使處理器恢復中斷程式的PSR 值,也就是恢復了程式的特權級別。當應用程式呼叫一個系統呼叫時,會發生類似的情況。對於Itanium,應用程式使用系統呼叫是通過以下方式實現的:把系統呼叫識別符號和引數放在一個預定義的區域,然後通過執行一個特殊的指令中斷使用者態程式的執行,並把控制權交給核心。

3.4.2 程序建立

討論了與程序相關的資料結構後,可簡單描述實際建立程序時的步驟。

一旦作業系統決定基於某種原因(見表3.1)建立一個新程序,可按以下步驟進行:

  • 給新程序分配一個唯一的程序識別符號。此時,在主程序表中增加一個新表項,表中的每個新表項對應著一個程序。
  • 程序分配空間。這包括程序映像中的所有元素。作業系統須知道私有使用者地址空間(程式和資料)和使用者棧需要多少空間。可以根據程序的型別使用預設值,也可以在作業建立時根據使用者請求設定。如果一個程序是由另一個程序生成的,則父程序可以把所需的值作為程序建立請求的一部分傳遞給作業系統。如果任何現有的地址空間被這個新程序共享,則必須建立正確的連線。最後,必須給程序控制塊分配空間。
  • 初始化程序控制塊。程序識別符號部分;處理器狀態資訊部分的大多數專案通常初始化成0,除了程式計數器(被置為程式入口點)和系統棧指標(用來定義程序棧邊界);程序控制資訊部分的初始化基於標準預設值和為該程序所請求的屬性。例如,程序狀態在典型情況下被初始化成就緒或就緒/掛起;除非顯式地請求更高的優先順序,否則優先順序的預設值為最低優先順序;除非顯式地請求或從父程序處繼承,否則程序最初不擁有任何資源(I/O 裝置、檔案)。
  • 設定正確的連線。例如,如果作業系統把每個排程佇列都儲存成連結串列,則新程序必須放置在就緒或就緒/掛起連結串列中。
  • 建立或擴充其他資料結構。例如,作業系統可能為每個程序儲存著一個記賬檔案,可用於編制賬單和/或進行效能評估。

3.4.3 程序切換

表面看程序切換的功能是簡單的。某一時刻,一個正在執行的程序被中斷,作業系統指定另一個程序為執行態,並把控制權交給這個程序。

  • 什麼事件觸發程序的切換?
  • 模式切換與程序切換之間的區別。
  • 為實現程序切換,作業系統須對它控制的各種資料結構做些什麼?

何時切換程序

程序切換在作業系統從當前正在執行的程序中獲得控制權的任何時刻發生。表3.8 給出了可能把控制權交給作業系統的事件。

一、系統中斷。大多數作業系統區分兩種型別的系統中斷:

  • 中斷,與當前正在執行的程序無關的某種型別的外部事件相關,如完成一次I/O 操作;
  • 陷阱,與當前正在執行的程序所產生的錯誤或異常條件相關,如非法的檔案訪問。

對於普通中斷,控制首先轉移給中斷處理器,它做一些基本的輔助工作,然後轉到與已經發生的特定型別的中斷相關的作業系統例程。參見以下例子:

  • 時鐘中斷:作業系統確定當前正在執行的程序的執行時間是否已經超過了最大允許時間段(時間片,即程序在被中斷前可以執行的最大時間段),如果超過了,程序必須切換到就緒態,調入另一個程序。
  • I/O 中斷:作業系統確定是否發生了I/O 活動。如果I/O 活動是一個或多個程序正在等待的事件,作業系統就把所有相應的阻塞態程序轉換到就緒態(阻塞/掛起態程序轉換到就緒/掛起態),作業系統必須決定是繼續執行當前處於執行態的程序,還是讓具有高優先順序的就緒態程序搶佔這個程序。
  • 記憶體失效:處理器訪問一個虛擬記憶體地址,且此地址單元不在記憶體中時,作業系統必須從外存中把包含這個引用的記憶體塊(頁或段)調入記憶體中。在發出調入記憶體塊的I/O 請求之後,作業系統可能會執行一個程序切換,以恢復另一個程序的執行,發生記憶體失效的程序被置為阻塞態,當想要的塊調入記憶體中時,該程序被置為就緒態。

二、對於陷阱,作業系統確定錯誤或異常條件是否是致命的。如果是,當前正在執行的程序被轉換到退出態,併發生程序切換;如果不是,作業系統的動作取決於錯誤的種類和作業系統的設計,其行為可以是試圖恢復或通知使用者,作業系統可能會進行一次程序切換或者繼續執行當前正在執行的程序。

三、作業系統可能被來自正在執行的程式的系統呼叫啟用。例如,一個使用者程序正在執行,並且正在執行一條請求I/O 操作的指令,如開啟檔案,這個呼叫導致轉移到作為作業系統程式碼一部分的一個例程上執行。通常,使用系統呼叫會導致把使用者程序置為阻塞態。

表3.8 程序執行的中斷機制

模式切換

中斷階段,處理器通過中斷訊號檢查是否發生了任何中斷。若沒有未處理的中斷,處理器繼續取指令週期,即取當前程序中的下一條指令,若存在未處理的中斷,處理器需要做以下工作:

  • 把程式計數器置成中斷處理程式的開始地址
  • 從使用者態切換到核心態,使得中斷處理程式碼可以包含有特權的指令

處理器繼續取指階段,並取中斷處理程式的第一條指令,它將給中斷提供服務。此時,被中斷的程序上下文儲存在被中斷程式的程序控制塊中

儲存的上下文環境包括什麼?所有中斷處理可能改變的資訊和恢復被中斷程式時所需要的資訊。因此,須儲存稱做處理器狀態資訊的程序控制塊部分,這包括程式計數器、其他處理器暫存器和棧資訊。

還需要做些其他工作嗎?取決於下一步會發生什麼。中斷處理程式通常執行與中斷相關的基本任務的小程式。例如,它重置表示出現中斷的標誌或指示器。可能給產生中斷的實體如I/O 模組傳送應答。它還做一些與產生中斷的事件結果相關的基本輔助工作。例如,如果中斷與I/O 事件有關,中斷處理程式將檢查錯誤條件;如果發生了錯誤,中斷處理程式給最初請求I/O操作的程序發一個訊號。如果是時鐘中斷,處理程式將控制移交給分派器,當分配給當前正在執行程序的時間片用盡時,分派器將控制轉移給另一個程序。

程序控制塊中的其他資訊如何處理?如果中斷之後是切換到另一個應用程式,則需要做一些工作。但是,在大多數作業系統中,中斷的發生並不是必須伴隨著程序切換的。可能是中斷處理器執行之後,當前正在執行的程序繼續執行。在這種情況下,所需要做的是當中斷髮生時儲存處理器狀態資訊,當控制返回給這個程式時恢復這些資訊。在典型情況下,儲存和恢復功能由硬體實現。

程序狀態的變化

顯然,模式切換與程序切換是不同的。發生模式切換可以不改變正處於執行態的程序狀態,這種情況下,儲存上下文環境和以後恢復上下文環境只需要很少的開銷。但是,如果當前正在執行的程序被轉換到另一個狀態(就緒、阻塞等),則作業系統必須使其環境產生實質性的變化,完整的程序切換步驟如下:

  • 儲存處理器上下文環境,包括程式計數器和其他暫存器。
  • 更新當前處於執行態程序的程序控制塊,包括將程序的狀態改變到另一狀態(就緒態、阻塞態、就緒/掛起態或退出態)。還必須更新其他相關域,包括離開執行態的原因和記賬資訊。
  • 將程序的程序控制塊移到相應的佇列(就緒、在事件i 處阻塞、就緒/掛起)。
  • 選擇另一個程序執行,這方面的內容將在本書的第四部分探討
  • 更新所選擇程序的程序控制塊,包括將程序的狀態變為執行態。
  • 更新記憶體管理的資料結構,這取決於如何管理地址轉換,這方面的內容將在第三部分探討。
  • 恢復處理器在被選擇的程序最近一次切換出執行狀態時的上下文環境,這可以通過載入程式計數器和其他暫存器以前的值來實現。

因此,程序切換涉及狀態變化,因而比模式切換需要做更多的工作。

3.5 作業系統的執行

作業系統的兩個特殊事實:

  • 作業系統與普通的計算機軟體以同樣的方式執行,也就是說,它也是由處理器執行的一個程式。
  • 作業系統經常釋放控制權,並且依賴於處理器恢復控制權。

若作業系統僅僅是一組程式,那麼作業系統是一個程序嗎?如果是,如何控制它?這些有趣的問題列出了大量的設計方法,圖3.15 給出了在當代各種作業系統中使用的各種方法。

3.5.1 無程序的核心

老作業系統中,傳統和通用的方法是在所有的程序之外執行作業系統核心(見圖3.15a)。通過這種方法,在當前正執行的程序被中斷或產生一個系統呼叫時,該程序的模式上下文環境被儲存起來,控制權轉交給核心。作業系統有自己的記憶體區域和系統棧,用於控制過程呼叫和返回。作業系統可以執行任何預期的功能,並恢復被中斷程序的上下文,這將導致被中斷的使用者程序重新繼續執行。或者,作業系統可以完成儲存程序環境的功能,並繼續排程和分派另一個程序,是否這樣做取決於中斷的原因和當前的情況。

無論哪種情況,其關鍵點是程序的概念僅僅適用於使用者程式,作業系統程式碼作為一個在特權模式下工作的獨立實體被執行。

3.5.2 在使用者程序中執行

在較小的機器的作業系統中,常見的方法是在使用者程序的上下文中執行幾乎所有作業系統軟體。其觀點是作業系統從根本上說是使用者呼叫的一組例程,在使用者程序環境中執行,用於實現各種功能,如圖3.15b 所示。在任何時刻,作業系統管理著n 個程序映像,每個映像不僅包括圖3.13 中列出的區域,而且還包括核心程式的程式、資料和棧區域。

圖3.15 作業系統和使用者程序的關係

圖3.16 給出了這個策略下的一個典型的程序映像結構。當程序在核心態下時,獨立的核心棧用於管理呼叫/返回。作業系統程式碼和資料位於共享地址空間中,被所有的使用者程序共享。

圖3.16 程序映像:操作
系統在使用者空間中執行

當發生一箇中斷、陷阱或系統呼叫時,處理器被置於核心態,控制權轉交給作業系統。為了將控制從使用者程式轉交給作業系統,需要儲存模式上下文環境並進行模式切換,然後切換到一個作業系統例程,但此時仍然是在當前使用者程序中繼續執行,因此,不需要執行程序切換,僅在同一個程序中進行模式切換。

如果作業系統完成其操作後,確定需要繼續運行當前程序,則進行一次模式切換,在當前程序中恢復被中斷的程式。該方法的重要優點之一是,一個使用者程式被中斷以使用某些作業系統例程,然後被恢復,所有這些不用以犧牲兩次程序切換為代價。如果確定需要發生程序切換而不是返回到先前執行的程式,則控制權被轉交給程序切換例程,這個例程可能在當前程序中執行,也可能不在當前程序中執行,這取決於系統的設計。在某些特殊的情況下,如當前程序必須置於非執行態,而另一個程序將指定為正在執行的程序。為方便起見,這樣一個轉換過程在邏輯上可以看做是在所有程序之外的環境中被執行的。

在某種程度上,對作業系統的這種看法是非常值得注意的。在某些時候,一個程序可以儲存它的狀態資訊,從就緒態程序中選擇另一個程序,並把控制權釋放給這個程序。之所以說這是一種混雜的情況,是因為在關鍵時候,在使用者程序中執行的程式碼是共享的作業系統程式碼而不是使用者程式碼。基於使用者態和核心態的概念,即使作業系統例程在使用者程序環境中執行,使用者也不能篡改或干涉作業系統例程。這進一步說明程序和程式的概念是不同的,它們之間不是一對一的關係。在一個程序中,使用者程式和作業系統程式都有可能執行,而在不同使用者程序中執行的作業系統程式是相同的。

3.5.3 基於程序的作業系統

圖3.15c 中顯示的是另一種選擇,即把作業系統作為一組系統程序來實現。與其他選擇一樣,作為核心一部分的軟體在核心態下執行。不過在這種情況下,主要的核心函式被組織成獨立的程序,同樣,還可能有一些在任何程序之外執行的程序切換程式碼。

這種方法有幾個優點。它利用程式設計原理,促使使用模組化作業系統,並且模組間具有最小的、簡明的介面。此外,一些非關鍵的作業系統函式可簡單地用獨立的程序實現,例如,很早就曾經提到過的用於記錄各種資源(處理器、記憶體、通道)的使用程度和系統中使用者程序的執行速度的監控程式。由於這個程式沒有為任何活動程序提供特定的服務,它只能被作業系統呼叫。作為一個程序,這個函式可以在指定的優先順序上執行,並且在分派器的控制下與其他程序交替執行。最後,把作業系統作為一組程序實現,在多處理器或多機環境中都是十分有用的,這時一些作業系統服務可以傳送到專用處理器中執行,以提高效能。

3.6 安全問題

作業系統對於每個程序都關聯了一套許可權。這些許可權規定了程序可以獲取哪些資源,包括記憶體區域、檔案和特權系統指令等。典型的是,一個程序的執行代表著一個使用者擁有作業系統認證的許可權。在配置的時候,一個系統或者是一個實用程序就被分配了許可權。

在一個典型的系統中,最高級別的許可權指的是管理員、超級使用者或根使用者的訪問許可權。根使用者的訪問許可權提供了對一個作業系統所有的功能和服務的訪問。一個有著根使用者訪問許可權的程序可以安全地控制一個系統,可以增加或者改變程式和檔案,對其他程序進行監控,傳送和接收網路流量和改變許可權。

設計任何作業系統的一個關鍵問題是阻止或者至少是探測一個使用者或者是一種惡意軟體(惡意程式)獲得系統授權的許可權的企圖,尤其是從根使用者獲取。本節,我們將簡短地總結關於這種安全問題的威脅和對策。在第七部分將對其做更加詳細的闡述。

3.6.1 系統訪問威脅

系統訪問威脅分為兩大類:入侵者和惡意軟體。

入侵者

對於安全,一個最普通的威脅就是入侵者(另外一個是病毒),通常是指黑客和解密高手。在早期的一項對入侵的重要研究中,Anderson [ANDE80] 定義了三種類型的入侵者:

冒充者:沒有授權的個人去使用計算機和通過穿透系統的訪問控制去使用一個合法使用者的賬號。

濫用職權者:一個合法的使用者訪問沒有授權的資料、程式或資源,或者使用者具有這種訪問的授權,但是濫用了他的許可權。

祕密使用者:一個使用者獲得了系統的管理控制,然後使用這種控制來逃避審計和訪問控制,或者廢止審查收集。

冒充者有可能就是外部人員;濫用職權者一般都是內部人員;祕密使用者可能是外部人員也可能是內部人員。

入侵者的攻擊有良性的也有嚴重的。在良性階段,許多人僅僅是想瀏覽網際網路並想知道在網際網路上到底有什麼。在嚴重階段,這些人嘗試著去讀許可權資料,修改未授權的資料或者是破壞系統。

入侵者的目的是獲得一個系統的訪問許可權,或是增加一個系統的許可權獲取的範圍。最初許多攻擊是利用了系統或軟體的漏洞,這些漏洞可以讓使用者執行可以開啟系統後門的程式碼。當程式以一定的許可權執行,入侵者可以利用如緩衝溢位區攻擊來獲得系統的訪問。將在7 章介紹緩衝區溢位攻擊。

此外,入侵者也可以嘗試獲取那些已經被保護的資訊。在一些情況下,資訊就是在表框裡面的使用者密碼。如果知道了一些使用者的密碼,那麼入侵者可以登入一個系統,然後執行合法使用者的所有許可權。

惡意軟體

計算機系統最為複雜的威脅可能就是利用計算機系統漏洞的程式。這些威脅被稱為惡意軟體,或者是惡意程式。在這一部分,我們將關注如編輯器、編譯器和核心級程式等應用程式以及通用程式的威脅。

惡意軟體分為兩大類:一種需要宿主程式,一種則是獨立的。對於前者,也被稱為寄生,其本質上是一些不能獨立於實際應用程式、通用程式或系統程式而存在的片段,例如病毒、邏輯炸彈和後門。後者則是獨立並可以被作業系統排程和執行的程式,例如蠕蟲和殭屍程式。

我們也可以對這些軟體威脅進行以下的區分:一種不能進行復制,而另外一種則可以。前者是通過觸發器啟用的程式或者程式片段,例如,邏輯炸彈、後門和殭屍程式。後者由一個程式片段或者一個獨立的程式構成,當其執行後,可能產生一個或者多個它自己的副本,這些副本將在同一系統或其他系統中被啟用,例如病毒和蠕蟲。

比較而言,惡意軟體可能是無害的,或者表現為一個或多個有害的操作,這些有害的操作包括毀壞記憶體裡的檔案和資料,通過繞開控制而獲得許可權訪問和為入侵者提供一種繞開訪問控制的方法。

3.6.2 對抗措施

入侵檢測

RFC2828(網路安全術語表)對入侵檢測的定義如下:入侵檢測是一種安全服務,通過監控和分析系統事件發現試圖通過未經授權的方法訪問系統資源的操作,並對這種操作提供實時或者準實時的警告。

入侵檢測系統(IDS)可以分為如下幾類:

基於宿主的IDS:對於可疑活動,監控單個宿主的特徵和發生在宿主上的事件。

基於網路的IDS:監控特定的網路段或者裝置網路流量,分析網路、傳輸和應用協議來辨別可疑活動。

IDS 由三個部分組成:

感應器:感應器負責收集資料。感應器的輸入可能是系統的任一部分,輸入可能包含了侵擾的證據。典型的感應器輸入包括網路包、日誌檔案和系統呼叫記錄。感應器收集這些資訊,並把這些資訊傳送給分析器。

分析器:分析器從一個或多個感應器或者其他分析器接受輸入資料。分析器負責確定入侵是否發生。這部分的輸出表明瞭一個入侵已經發生。輸出可能包含了支援入侵發生的結論的證據。分析器可能提供對於入侵結果採取何種操作的指導。

使用者介面:IDS 的使用者介面可以讓使用者檢視系統的輸出和控制系統的行為。在一些系統中,使用者介面可以等同於負責人、控制器或者控制檯部分。

入侵檢測系統用來偵測人類入侵者行為,以及惡意軟體的行為。

認證

在許多電腦保安內容中,使用者認證是一個主要的構建模組和最初防線。使用者認證是許多種訪問控制和使用者責任的主要部分。RFC2828 對使用者認證做了如下定義:

系統實體定義了驗證和確認的過程。認證過程包括以下兩步:

確認步驟:對於安全系統,提出了識別符號。(應小心地分配識別符號,對於訪問控制服務等其他安全服務,認證定義是基本部分。)

驗證步驟:提出或產生認證資訊,用來證實在實體與識別符號之間的繫結。

例如,使用者Alice Toklas 擁有一個使用者識別符號ABTOKLAS。識別符號資訊可能被儲存在Alice想要使用的任意一臺伺服器或者計算機系統中,而且系統管理員和其他使用者可能知道這些資訊。一種典型的與使用者ID 相關聯的認證資訊項就是密碼,密碼是用於保守祕密的(祕密僅僅被Alice和系統所知)。如果沒有人得到或猜出Alice 的密碼,管理員可以通過Alice 的使用者ID 和密碼的結合建立Alice 的訪問許可,並稽核Alice 的操作。由於Alice 的ID 不是祕密,系統使用者可以給她傳送電子郵件,但是由於她的密碼是保密的,所以沒有人可以冒充成Alice。

本質上,識別是這樣一種方法:使用者向系統提供一個宣告的身份;使用者認證就是確認宣告的合法性的方法。

一共有4 種主要的認證使用者身份的方法,它們既可以單獨使用,也可以聯合使用:

個人知道的一些事物:例如包括密碼、個人身份號碼(PIN)或者是預先安排的一套問題的答案。

個人擁有的一些事物:例如包括電子通行卡、智慧卡、物理鑰匙。這種型別的身份驗證稱為令牌。

個人自身的事物(靜態生物識別技術):例如包括指紋、虹膜和人臉的識別。

個人要做的事物(動態生物識別技術):例如包括語音模式、筆跡特徵和輸入節奏的識別。

適當的實現和使用所有的這些方法,可以提供可靠的使用者認證。但是每個方法都有問題,使得對手能夠猜測或盜取密碼。類似地,使用者能夠偽造或盜取令牌。使用者可能忘記密碼或丟失令牌。而且,對於管理系統上的密碼、令牌資訊和保護系統上的這些資訊,還存在顯著的管理開銷。對於生物識別技術,也有各種各樣的問題,包括誤報和假否定、使用者接受程度、費用和便利與否。

訪問控制

訪問控制實現了一種安全策略:指定誰或何物(例如程序的情況)可能有權使用特定的系統資源和在每個場景下被允許的訪問型別。

訪問控制機制調節了使用者(或是代表使用者執行的程序)與系統資源之間的關係,系統資源包括應用程式、作業系統、防火牆、路由器、檔案和資料庫。系統首先要對尋求訪問的使用者進行認證。通常,認證功能完全決定了使用者是否被允許訪問系統。然後訪問控制功能決定了是否允許使用者的特定訪問要求。安全管理員維護著一個授權資料庫,對於允許使用者對何種資源採用什麼樣的訪問方式,授權資料庫做了詳細說明。訪問控制功能參考這個資料庫來決定是否准予訪問。稽核功能監控和記錄了使用者對於系統資源的訪問。

防火牆

對於保護本地系統或系統網路免於基於網路的安全威脅,防火牆是一種有效的手段,並且防火牆同時提供了經過廣域網和網際網路對外部網路的訪問。傳統上,防火牆是一種專用計算機,是計算機與外部網路的介面;防火牆內部建立了特殊的安全預防措施用以保護網路中計算機上的敏感檔案。其被用於服務外部網路,尤其是網際網路、連線和撥號線路。使用硬體或軟體來實現,並且與單一的工作站或者PC 連線的個人防火牆也很常見。

[BELL94]列舉了如下防火牆的設計目標:

1)從內部到外部的通訊必須通過防火牆,反之亦然。通過對除經過防火牆之外本地網路的所有訪問都進行物理阻塞來達到目的。可以對其進行各種各樣的配置,將在本章的後面部分進行闡述。

2)僅僅允許本地安全策略定義的授權通訊通過。使用的不同型別防火牆是通過不同型別的安全策略實現的。

3)防火牆本身對於滲透是免疫的。這意味著在一個安全的作業系統上使用強固系統。值得信賴的計算機系統適合用作防火牆,並且在管理應用中也被要求使用。

3.7 UNIX SVR4 程序管理

UNIX 系統V 使用了一種簡單但是功能強大的程序機制,且對使用者可見。UNIX 採用圖3.15b 中的模型,其中大部分作業系統在使用者程序環境中執行。UNIX 使用兩類程序,即系統程序和使用者程序。系統程序在核心態下執行,執行作業系統程式碼以實現管理功能和內部處理,如記憶體空間的分配和程序交換;使用者程序在使用者態下執行以執行使用者程式和實用程式,在核心態下執行以執行屬於核心的指令。當產生異常(錯誤)或發生中斷或使用者程序發出系統呼叫時,使用者程序可進入核心態。

3.7.1 程序狀態

UNIX 作業系統中共有9 種程序狀態,如表3.9 所示。圖3.17(基於[BACH86]中的圖)是相應的狀態轉換圖,它與圖3.9b 類似,有兩個UNIX 睡眠狀態對應於圖3.9b 中的兩個阻塞狀態,其區別可簡單概括如下:

UNIX 採用兩個執行態表示程序在使用者態下執行還是在核心態下執行。

UNIX 區分記憶體中就緒態和被搶佔態這兩個狀態。從本質上看,它們是同一個狀態,如圖中它們之間的虛線所示,之所以區分這兩個狀態是為了強調進入被搶佔狀態的方式。當一個程序正在核心態下執行時(系統呼叫、時鐘中斷或I/O 中斷的結果),核心已經完成了其任務並準備把控制權返回給使用者程式時,就可能會出現搶佔的時機。這時,核心可能決定搶佔當前程序,支援另一個已經就緒並具有較高優先順序的程序。在這種情況下,當前程序轉換到被搶佔態,但是為了分派處理,處於被搶佔態的程序和處於記憶體中就緒態的程序構成了一條佇列。

圖3.17 UNIX 程序狀態轉換圖

只有當程序準備從核心態移到使用者態時才可能發生搶佔,程序在核心態下執行時是不會被搶佔的,這使得UNIX 不適用於實時處理。有關實時處理需求的討論請參見第10 章。

UNIX 中有兩個獨特的程序。程序0 是一個特殊的程序,是在系統啟動時建立的。實際上,這是預定義的一個數據結構,在啟動時刻被載入,是交換程序。此外,程序0 產生程序1,稱做初始程序,程序1 是系統中的所有其他程序的祖先。當新的互動使用者登入到系統時,由程序1為該使用者建立一個使用者程序。隨後,使用者程序可以建立子程序,從而構成一棵分支樹,因此,任何應用程式都是由一組相關程序組成的。

表3.9 UNIX 程序狀態

3.7.2 程序描述

UNIX 中的程序是一組相當複雜的資料結構,它給作業系統提供管理程序和分派程序所需要的所有資訊。表3.10 概括了程序映像中的元素,它們被組織成三部分:使用者上下文、暫存器上下文和系統級上下文。

表3.10 UNIX 程序映像

使用者級上下文包括使用者程式的基本成分,可以由已編譯的目標檔案直接產生。使用者程式被分成正文和資料兩個區域,正文區是隻讀的,用於儲存程式指令。當程序正在執行時,處理器使用使用者棧進行過程呼叫和返回以及引數傳遞。共享記憶體區是與其他程序共享的資料區域,它只有一個物理副本,但是通過使用虛擬記憶體,對每個共享程序來說,共享記憶體區看上去好像在它們各自的地址空間中一樣。當程序沒有執行時,處理器狀態資訊儲存在暫存器上下文中。

系統級上下文包含作業系統管理程序所需要的其餘資訊,它由靜態部分和動態部分組成,靜態部分的大小是固定的,貫穿於程序的生命週期;動態部分在程序的生命週期中大小可變。靜態部分的一個成分是程序表項,這實際上是由作業系統維護的程序表的一部分,每個程序對應於表中的一項。程序表項包含對核心來說總是可以訪問到的程序控制資訊。因此,在虛擬記憶體系統中,所有的程序表項都在記憶體中,表3.11 中列出了程序表項的內容。使用者區,即U 區,包含核心在程序的上下文環境中執行時所需要的額外的程序控制資訊,當程序調入或調出記憶體時也會用到它。表3.12 給出了這個表的內容。

表3.11 UNIX 程序表項

(續)


表3.12 UNIX 的U 區

程序表項和U 區的區別反映出UNIX 核心總是在某些程序的上下文環境中執行,大多數時候,核心都在處理與該程序相關的部分,但是,某些時候,如當核心正在執行一個排程演算法,準備分派另一個程序時,它需要訪問其他程序的相關資訊。當給定程序不是當前程序時,可以訪問程序控制表中的資訊。

系統級上下文靜態部分的第三項是本程序區表,它由記憶體管理系統使用。最後,核心棧是系統級上下文環境的動態部分,當程序正在核心態下執行時需要使用這個棧,它包含當發生過程呼叫或中斷時必須儲存和恢復的資訊。

3.7.3 程序控制

UNIX 中的程序建立是通過核心系統呼叫fork()實現的。當一個程序產生一個fork 請求時,作業系統執行以下功能[BACH86]:

1)為新程序在程序表中分配一個空項。

2)為子程序賦一個唯一的程序識別符號。

3)做一個父程序上下文的邏輯副本,不包括共享記憶體區。

4)增加父程序擁有的所有檔案的計數器,以表示有一個另外的程序現在也擁有這些檔案。

5)把子程序置為就緒態。

6)向父程序返回子程序的程序號;對子程序返回零。

所有這些操作都在父程序的核心態下完成。為當核心完成這些功能後可以繼續下面三種操作之一,它們可以認為是分派器例程的一個部分:

在父程序中繼續執行。控制返回使用者態下父程序進行fork 呼叫處。

處理器控制權交給子程序。子程序開始執行程式碼,執行點與父程序相同,也就是說在fork呼叫的返回處。

控制轉交給另一個程序。父程序和子程序都置於就緒狀態。

很難想象這種建立程序的方法中父程序和子程序都執行相同的程式碼。其區別在於:當從fork中返回時,測試返回引數,如果值為零,則它是子程序,可以轉移到相應的使用者程式中繼續執行;如果值不為零,則它是父程序,繼續執行主程式。

3.8 小結

現代作業系統中最基本的構件是程序,作業系統的基本功能是建立、管理和終止程序。當程序處於活躍狀態時,作業系統必須設法使每個程序都分配到處理器執行時間,並協調它們的活動、管理有衝突的請求、給程序分配系統資源。

為執行程序管理功能,作業系統維護著對每個程序的描述,或者稱為程序映像,它包括執行程序的地址空間和一個程序控制塊。程序控制塊含有作業系統管理程序所需要的所有資訊,包括它的當前狀態、分配給它的資源、優先順序和其他相關資料。

在整個生命週期中,程序總是在一些狀態之間轉換。最重要的狀態有就緒態、執行態和阻塞態。一個就緒態程序是指當前沒有執行但已做好了執行準備的程序,只要作業系統排程到它就立即可以執行;執行態程序是指當前正在被處理器執行的程序,在多處理器系統中,會有多個程序處於這種狀態;阻塞態程序正在等待某一事件的完成,如一次I/O 操作。

一個正在執行的程序可被一個在程序外發生且被處理器識別的中斷事件打斷,或者被執行作業系統的系統呼叫打斷。不論哪種情況,處理器都執行一次模式切換,把控制轉交給作業系統例程。作業系統在完成必需的操作後,可以恢復被中斷的程序或者切換到別的程序。

轉載於:https://my.oschina.net/cqlcql/blog/689229