Windows 程式設計/使用者模式與核心模式
在Windows(以及大多數現代作業系統)中,執行在“使用者模式”下的程式碼和執行在“核心模式”下的程式碼之間存在區別。本章將指明一些差異。首先,Intel CPU 具有被稱為“環”的操作模式,它們指定了可供執行程式碼使用的指令和記憶體型別。總共有四個環
- 環 0(也稱為核心模式)擁有對所有資源的完全訪問許可權。它是 Windows 核心執行的模式。
- 環 1 和 2 可以用訪問級別進行自定義,但通常未使用,除非有虛擬機器執行。
- 環 3(也稱為使用者模式)對資源的訪問許可權有限。
之所以如此設計,是因為如果所有程式都在核心模式下執行,它們將能夠覆蓋彼此的記憶體,並在崩潰時可能導致整個系統崩潰。

當程式啟動(例如網頁瀏覽器或文字處理器)時,它會在自己的程序中執行。程序包含它自己的“虛擬”記憶體空間和資源。它的記憶體是“虛擬的”,因為程序認為在地址0x12345678處的記憶體實際上可能在物理記憶體中的地址0x65f7a678處。類似地,兩個不同的程序可能在(對於它們來說)0x00401000處儲存了不同的資料。這是透過將記憶體劃分為稱為頁的塊來實現的;在 x86 系統上,一頁的大小為 4 KB。每個頁面可以擁有自己的一組屬性,例如只讀/讀寫。CPU 擁有一個透明的機制,透過頁表將虛擬地址轉換為物理地址,而頁表是由作業系統設定的。
虛擬記憶體有很多用處
- 程序無法訪問其他程序的記憶體,
- 每個頁面可以擁有不同的保護設定(只讀或讀寫,僅核心模式),以及
- 程序的非活動記憶體區域可以“分頁出”(儲存)到頁面檔案,並在需要時由作業系統檢索。當系統物理記憶體不足時,也會這樣做。
Windows 啟動的每個程序(除了系統“程序”)都在使用者模式下執行。在這種模式下,程式無法直接修改分頁,因此除了透過 API 函式外,無法訪問其他程式的記憶體。使用者模式下的程式也不能干擾中斷和上下文切換。
當 Windows 首次載入時,Windows 核心會啟動。它在核心模式下執行,並設定分頁和虛擬記憶體。然後,它建立一些系統程序,並允許它們在使用者模式下執行。那麼,CPU 如何才能切換回核心模式呢?這不是由 CPU 自動完成的。CPU 經常受到某些事件(計時器、鍵盤、硬碟 I/O)的干擾,這些事件被稱為中斷。核心必須首先設定中斷處理程式來處理這些事件。然後,每當中斷髮生時,CPU 就會停止執行當前執行的程式,立即切換到核心模式,並執行該事件的中斷處理程式。處理程式會儲存 CPU 的狀態,執行與該事件相關的某些處理,並恢復 CPU 的狀態(可能切換回使用者模式),以便 CPU 可以恢復執行程式。
當程式想要呼叫 Windows API 函式1時,它會觸發一箇中斷2,這會導致 CPU 切換到核心模式,並開始執行所需的 API 函式。當 API 函式完成處理後,它會切換回使用者模式,並恢復執行程式。這是因為像ReadProcessMemory這樣的 API 函式無法在使用者模式下工作;程式無法訪問其他程式的記憶體。但在核心模式下,API 函式可以無限制地讀取任何記憶體區域。
1. 實際上,Windows API 函式最終會呼叫另一個 API:本地 API。這是 Windows NT 核心系列使用的 API。此時CPU 會切換到核心模式。
2. 現代 CPU 擁有用於系統呼叫的特殊、更快的指令,例如 x86 上的sysenter和sysexit。這些指令會導致 CPU 切換到環 0,然後開始執行由作業系統設定的處理程式。
因此,程式執行並呼叫 API 函式。那麼,其他程式如何獲得執行的機會呢?大多數情況下,程式只是允許作業系統切換到另一個程式,因為它們正在等待某些東西(使用者輸入、硬碟)。這些程式被稱為不可執行程式,由於它們呼叫核心來等待某些東西,因此核心知道要執行上下文切換以允許另一個程式執行。這是透過以下方式完成的
- 儲存當前程式的狀態(包括暫存器),
- 確定下一個要執行的程式,
- 並恢復另一個程式的狀態。
如果一個程式(更準確地說是執行緒或程序)執行時間超過了一定時間(執行緒量子或程序的時間片),作業系統將上下文切換到另一個程式。這個概念被稱為搶佔。搶佔是透過在處理器中設定一個定時中斷來實現的,該中斷將呼叫上下文切換。用於每個程序的時間片可能不同。