啟動過程是我們瞭解作業系統的第一個環節。瞭解 Windows 的啟動過程,可以幫助我們解決一些啟動的問題,也能幫助我們瞭解 Windows 的整體結構。
以下內容將分為【載入核心】、【核心初始化】和【應用程式初始化】三個部分。
如 啟動過程概覽 所示,載入過程分為兩種方式。傳統的 BIOS(Basic Input/Output System)和 UEFI(Unified Extensible Firmware Interface)。這兩種方式大致相似,本文以 BIOS 為例來說明引導說過。
當開機鍵被按下後,電源將執行初始化,確保電源可靠後,主機板上的硬體電路給 CPU 發覆位(reset)訊號。
CPU 收到復位訊號後,將從 RAM 中讀取指令執行。此時記憶體中是沒有任何指令需要執行的。於是 CPU 就去 BIOS ROM 中特定的位置(FFFF0H)中執行指令,這個指令是一個跳轉指令 ,將告訴 CPU 真正的 BIOS 程式所在的位置。跳轉指令如下:
jmp far f000:e05b ; 跳轉到0xfe05b執行
BIOS 主要包含以下四個部分的程式:
Windows 的做法是,讓引導磁區中的程式碼讀入其他磁區的資料,然後跳轉到下一個磁區的程式碼區。這樣就可以不受單個引導磁區長度的限制,這種做法相當於將第一個引導磁區當做一個載入器(loader),而真正完成引導磁區功能的磁區隨後被載入進來並執行。這一過程對於 MBR 是透明的,從而保持良好的相容性。
此時程式碼的控制權已經來到了 ntldr(也可以是 WinLoad 或者 os loader),此時處理器還執行真真實模式下,所以 ntldr 也分為兩部分:真真實模式程式碼和保護模式程式碼。
在真真實模式下,ntldr 的主要任務是:
在保護模式下,需要完成以下步驟:
typedef struct _LOADER_PARAMETER_BLOCK {
LIST_ENTRY LoadOrderListHead; // 載入的模組連結串列,每個元素都為 KLDR_DATA_TABLE_ENTRY
LIST_ENTRY MemoryDescriptorListHead; // 記憶體描述符連結串列,每個元素都為 MEMORY_ALLOCATION_DESCRIPTOR
LIST_ENTRY BootDriverListHead;// 引導驅動程式連結串列,每個元素都為 BOOT_DRIVER_LIST_ENTRY
ULONG_PTR KernelStack;// 核心棧頂
ULONG_PTR Prcb;// 程序環境,指向一個過程控制塊
ULONG_PTR Process;// 初始程序,EPROCESS
ULONG_PTR Thread;// 初始執行緒,ETHREAD
ULONG RegistryLength;// System 儲巢的長度
PVOID RegistryBase;// System 儲巢的基地址
PCONFIGURATION_COMPONENT_DATA ConfigurationRoot;// 設定樹,包含 ISA、磁碟和 ACPI 的設定資料
PCHAR ArcBootDeviceName;// 引導分割區的 ARC 名稱
PCHAR ArcHalDeviceName;// 系統分割區的 ARC 名稱
PCHAR NtBootPathName;// OS 目錄的路徑名稱,比如「\Windows」
PCHAR NtHalPathName;// OS 載入器的路徑名稱,比如「\」
PCHAR LoadOptions;// 引導選項,來自 boot.ini
PNLS_DATA_BLOCK NlsData;// 包含 ANSI 內碼錶、OEM 內碼錶和 Unicode 碼錶
PARC_DISK_INFORMATION ArcDiskInformation;// 所有磁碟的簽名結構
PVOID OemFontFile;// OEM 字型檔案
struct _SETUP_LOADER_BLOCK *SetupLoaderBlock;// 網路引導或文字模式安裝引導
PLOADER_PARAMETER_EXTENSION Extension;// 擴充套件部分
union {
I386_LOADER_BLOCK I386;
// ALPHA_LOADER_BLOCK Alpha;
// IA64_LOADER_BLOCK Ia64;
} u;
} LOADER_PARAMETER_BLOCK, *PLOADER_PARAMETER_BLOCK;
到這裡,接下來的過程,我們就可以通過 WRK 來觀察具體的過程了。
ntoskrnl.exe 的入口函數是 _KiSystemStartup,可以順著這個方法往下看。
整個初始化分為兩個階段:Phase0 和 Phase1。
階段1的初始化入口是 Phase1InitializationDiscard 方法。此方法也是做了各種初始化,具體可以去看程式碼。由於其時間比較長,所以內部做了一個進度估計。
待 階段1 完成初始化,執行體的各個元件都進入了一個正常狀態。但只有核心是沒有意義的,系統必須讓應用程式跑起來。應用程式的啟動就得依賴剛啟動的 smss 程序繼續了。
現在控制權來到了 smss,smss 是 Windows 中一個重要的程序。
smss 還會繼續完成引導過程。在核心初始化期間,只有 System 儲巢被載入到記憶體當中了。其他的就得由 smss 來載入。其他初始化必要的工作,可以參看 HKLM\SYSTEM\CurrentControlSet\Control\Session Manager 。
登入檔中能發現一些有趣的子鍵。
然後 smss 便會啟動 winlogon 程序,隨後接下來的引導便交由 winlogon 程序了。 winlogon 主要職責如下:
在登入過程的最後,winlogon 檢查登入檔 HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon\Userinit 的值,並建立一個程序來執行該值字串。該值串的預設值為 userinit.exe 程式的路徑。Userinit 程序載入當前登入使用者的輪廓,然後檢查 HKCU\Software\Microsoft\Windows NT\CurrentVersion\Winlogon\Shell 的值,並建立一個程序來執行該值字串;如果該值不存在,則執行 HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon\Shell 的值,其預設值為 explorer.exe。然後,userinit 程序退出。由於當前登入對談的 Shell 程式(explorer.exe)已經啟動,因此使用者可以在桌面上操作了。
至此,引導就全部結束了。