大多數處理器至少支持兩種執行模式。某些指令只能在特權模式下執行,包括讀取或者改變諸如程序狀態字之類控制寄存器的指令、原始IO指令和內存管理相關的指令。另外,有一部分內存區域僅在特權下可以被訪問到。
非特權態常被稱為用戶態,這是因為用戶程序通常在該模式下執行;特權態可稱作系統態、控制態或者內核態,內核態指的是操作系統的內核,這是操作系統中包含重要系統功能的部分。
這樣產生了兩個問題:處理器如何知道它正在什么模式下執行以及如何改變這一模式。對第一個問題,程序狀態字中有一個位表示執行模式,這一位應某些事件的要求而改變。在典型情況下,當用戶調用一個操作系統服務或者中斷觸發系統例程的執行時,執行模式被設置成內核態,當從系統服務返回到用戶進程時,執行模式被設置成內核態,當從系統服務返回到用戶態是,執行模式被設置成為用戶態。例如IA64的PSR,CPL級別0為特權級別。對於intel Itanium,應用程序使用系統調用是通過以下方式實現的:把系統調用標示符和參數放在一個預定義的區域,然后執行一個特殊的指令中斷用戶態程序的執行,並把控制權交給內核。
一旦操作系統決定創建一個新進程,它就可以按以下步驟進行:
1)給新進程分配一個唯一的進程標示符。此時,在主進程表中增加一個新表項,表中的每個新表項對應着一個進程。
2)給進程分配空間。這包括進程映像中的所有元素。因此,操作系統必須知道私有用戶地址空間(程序和數據)和用戶空間棧需要多少空間。可以根據進程的類型使用默認值,也可以在作業創建時根據用戶請求設置。如果一個進程是由另一個進程創建的,則用戶進程可以把所需的值作為進程創建請求的一部分傳遞給操作系統。如果任何現有的地址空間被這個新進程共享,則必需建立正確的連接。最后,必須給進程控制塊分配空間。
3)初始化進程控制塊。進程標識符部分包括進程ID和其他相關的ID,如父進程的ID等;處理器狀態信息部分的大多數項目通常初始化成0,除了程序計數器(被置為程序入口點)和系統棧指針(用來定義進程棧邊界);進程控制信息部分的初始化基於標准默認值和為該進程所請求的屬性。例如,進程狀態在典型的情況下被初始化成就緒/掛起;除非顯式地請求更高的優先級,否則優先級的默認值為最低優先級;除非顯式地請求或者從父進程繼承,否則進程最初不擁有任何資源(IO設備、文件)。
4)設置正確的連接。例如,如果操作系統把每個調度隊列都保存成鏈表,則新進程必須放在就緒或者就緒掛起鏈表中。
5)創建或者擴充其他數據結構。例如,操作系統可能為每個進程保存一個記賬文件,可用於編制賬單和進行性能評估。
進程切換
從表面看,進程切換的功能是很簡單的。在某一時刻,一個正在進行的進程被中斷,操作系統指定另一個進程為運行態,並把控制權交給這個進程。但是這會引發若干問題。首先,什么事件觸發進程的切換?另一個問題是必須認識到模式切換和進程切換之間的區別。最后,為實現進程切換,操作系統必須對它控制的各種數據結構做些什么?
何時切換進程
進程切換可以在操作系統從當前正在運行的進程中獲得控制權的任何時刻發生。首先考慮系統中斷。實際上,大多數操作系統區分兩種類型的系統中斷。一種稱為中斷,另一種稱為陷阱。前者與當前正在運行的進程無關的某種類型的外部事件有關,如完成一次IO操作;后者與當前正在運行的進程所產生的錯誤或者異常條件有關,如非法的文件訪問。對於普通中斷,控制首先轉移給中斷處理器,它做一些基本的輔助工作,然后轉到與已經發生的特定類型的中斷相關的操作系統例程。參見以下例子:
時鍾中斷:操作系統確定當前正在運行的進程的執行時間是否已經超過了最大允許的時間段(時間片,即進程在被中斷前可以執行的最大時間段),如果超過了,進程必須切換到就緒態,調用另一個進程。
IO中斷:操作系統確定是否發生了IO活動。如果IO活動是一個或者多個進程正在等待的事件,操作系統就把所有相應的阻塞態進程轉換到就緒態,阻塞掛起轉到就緒掛起,操作系統必須決定是繼續執行當前處於運行態的進程,還是讓具有高優先級的就緒態進程搶占這個進程。
內存失效:處理器訪問一個虛地址,且此地址單元不在內存中時,操作系統必須從外存中把包含這個引用的內存塊(段或頁)調入內存中。在發出調入內存塊的IO請求之后,操作系統可能會執行一個進程切換,以恢復另一個進程的執行,發生內存失效的進程被置為阻塞態,當想要的塊調入內存中時,該進程被置為就緒態。
對於陷阱,操作系統確定錯誤或者異常條件是否是致命傷。如果是,當前正在運行的進程被轉換到退出態,並發生進程切換;如果不是,操作系統的動作取決於錯誤的種類和操作系統的設計,其行為可以是試圖恢復或通知用戶,操作系統可能會進行一次進程切換或者繼續執行當前正在運行的進程。
最后,操作系統可能會被來自正在執行的程序的系統調用激活。例如,一個用戶進程正在運行並且正在執行一條請求IO操作的指令,如打開文件,這個操作導致轉移到作為操作系統代碼一部分的一個例程上執行。通常,使用系統調用會導致把用戶進程置位阻塞態。
模式切換
如果存在一個未處理的中斷,處理器需要做以下工作:
1)把程序計數器置成中斷處理程序的開始地址。
2)從用戶態切換到內核態,使得中斷處理代碼可以包含特權的指令。
處理器現在繼續取指階段,並取中斷處理程序的第一條指令,他將給中斷提供服務。此時,被中斷的進程上下文保存在被中斷程序的PCB中。
問題是,保存的上下文環境包括什么?答案是它必須包括所有中斷處理可能改變的信息和恢復被中斷程序時所需要的信息。因此,必須保存稱作處理器狀態信息的進程控制塊部分,這包括程序計數器、其他處理器寄存器和棧信息。
還需要做其他工作嗎?這取決於下一步會發生什么。中斷處理程序通常是執行一些與中斷相關的基本任務的小程序。例如,它重置表示出現中斷的標志或指示器。可能給產生中斷的實體如IO模塊發送應答。它還做一些與產生中斷的事件結果相關的基本輔助工作。例如,如果中斷與IO事件有感,中斷處理程序將檢查錯誤條件;如果發生了錯誤,中斷處理程序給最初請求IO操作的進程發一個信號。如果是時鍾中斷,處理程序將控制移交給調度器,當分配給當前正在運行的時間片用盡時,調度器將控制轉移給另一個進程。
進程控制塊中的其他信息如何處理?如果中斷之后是切換到另一個應用程序,則需要做一些工作。但是,在大多數操作系統中,中斷的發生並不是伴隨着進程切換的。可能是中斷處理執行之后,當前正在運行的進程繼續執行。在這種情況下,所需要做的是當中斷發生時保存處理器狀態信息。,當控制權返回給這個程序時回復這些信息。在典型情況下,保存和恢復功能由硬件實現。
進程狀態的變化
顯然,模式切換與進程切換是不同的。發生模式切換可以不改變正處於運行態的進程狀態,在這種情況下,保存上下文環境和以后恢復上下文環境只需要很少的開銷。但是,如果當前正在運行的進程被切換到另一個狀態(就緒、阻塞等),則操作系統必須使其環境產生實質性的變化,完整的進程切換步驟如下:
1)保存處理器上下文,包括程序計數器和其他寄存器。
2)更新當前處於運行態的進程的進程控制塊,包括將進程的狀態改變到另一狀態(就緒態、阻塞態、就緒/掛起態或退出態)。還必須更新其他相關域,包括離開運行態的原因和記賬信息。
3)將進程的進程控制塊移到相應的隊列(就緒、在事件i處阻塞、就緒/掛起)。
4)選擇另一個進程執行。
5)更新所選擇進程的進程控制塊,包括將進程的狀態轉變為運行態。
6)更新內存管理的數據結構,這取決於如何管理地址轉換。
7)恢復處理器在被選擇的進程最后一次切換出運行狀態的上下文環境,者可通過載入程序計數器和其他寄存器以前的值來實現。
進程切換涉及狀態變化,因而比模塊切換需要做更多的工作。