引言
啟動過程是我們瞭解作業系統的第一個環節。瞭解 Windows 的啟動過程,可以幫助我們解決一些啟動的問題,也能幫助我們瞭解 Windows 的整體結構。
以下內容將分為【載入核心】、【核心初始化】和【應用程式初始化】三個部分。
載入核心
如 啟動過程概覽 所示,載入過程分為兩種方式。傳統的 BIOS(Basic Input/Output System)和 UEFI(Unified Extensible Firmware Interface)。這兩種方式大致相似,本文以 BIOS 為例來說明引導說過。
載入 BIOS
當開機鍵被按下後,電源將執行初始化,確保電源可靠後,主機板上的硬體電路給 CPU 發覆位(reset)訊號。
CPU 收到復位訊號後,將從 RAM 中讀取指令執行。此時記憶體中是沒有任何指令需要執行的。於是 CPU 就去 BIOS ROM 中特定的位置(FFFF0H)中執行指令,這個指令是一個跳轉指令 ,將告訴 CPU 真正的 BIOS 程式所在的位置。跳轉指令如下:
jmp far f000:e05b ; 跳轉到0xfe05b執行
執行 BIOS
BIOS 主要包含以下四個部分的程式:
- POST:Power On Self Test。用於檢測硬體的可用性,如果有問題,會發出蜂鳴聲並且停止進行下一步。
- Bootstrap Loader:用來確定作業系統的位置,幫助引導作業系統的啟動。此程式會遍歷所有的儲存裝置的0盤0道1扇區的內容,如果這一扇區最後兩位元組的內容是0x55 0xaa,則認為它是啟動區(也就是 MBR,Master Boot Record),並將扇區的內容(也就是ntldr)複製到記憶體0x7c00位置。至此,程式碼的控制權就交給了作業系統程式碼了。
Windows 的做法是,讓引導扇區中的程式碼讀入其他扇區的資料,然後跳轉到下一個扇區的程式碼區。這樣就可以不受單個引導扇區長度的限制,這種做法相當於將第一個引導扇區當做一個載入器(loader),而真正完成引導扇區功能的扇區隨後被載入進來並執行。這一過程對於 MBR 是透明的,從而保持良好的相容性。
- BIOS:用於軟體和硬體的通訊。
- CMOS Setup:一個配置程式,用於配置作業系統的時間、日期和密碼等
載入 NT 核心
此時程式碼的控制權已經來到了 ntldr(也可以是 WinLoad 或者 os loader),此時處理器還執行真實模式下,所以 ntldr 也分為兩部分:真實模式程式碼和保護模式程式碼。
在真實模式下,ntldr 的主要任務是:
- 完成在真實模式下執行的初始化。比如清除鍵盤緩衝區。
- 為切換到保護模式做好基本的環境準備。
- 將處理器切換到保護模式下,然後將控制權交給保護模式下的程式碼。
在保護模式下,需要完成以下步驟:
- 由於虛擬機器制轉譯機制還未就緒,首先要做的就是把實體記憶體管理起來。採用一個記憶體描述符陣列將每一段記憶體的大小和用途記錄下來,然後開啟頁面對映機制
- 繼續執行其他的初始化工作。包含 I/O 裝置的初始化等
- 從系統分割槽的根目錄下讀取 boot.ini 檔案。並檢查是否存在有效的 hiberfil.sys 檔案,如果存在,則將引導過程轉移到休眠系統的恢復過程。否則,將解析 boot.ini 檔案 。
- 執行 NTDETECT.COM 程式。此程式執行在真實模式下,它將利用 BIOS 來查詢系統的基本裝置和配置資訊,並收集起來。在引導後期,將其存放到登錄檔 HKLM\HARDWARE\DESCRIPTION 下。
- 載入 ntoskrnl.exe、hal(預設為 hal.dll,具體是哪個映像檔案,在 boot.ini 中會有記錄),再通過載入登錄檔的 system 儲巢,即 WINDOWS\system32\config\system 檔案 拿到哪些裝置驅動程式必須被載入進來,然後再把必要的裝置驅動載入進來。
- 此時載入的準備工作基本就緒,將準備的資訊構造成一個引數塊 LOADER_PARAMETER_BLOCK.如下定義來自 WRK。
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;
- 最後,ntlrd 將控制權交給 ntoskrnl.exe 的入口函式。表示核心載入完成。
核心初始化
到這裡,接下來的過程,我們就可以通過 WRK 來觀察具體的過程了。
ntoskrnl.exe 的入口函式是 _KiSystemStartup,可以順著這個方法往下看。
整個初始化分為兩個階段:Phase0 和 Phase1。
階段1的初始化入口是 Phase1InitializationDiscard 方法。此方法也是做了各種初始化,具體可以去看程式碼。由於其時間比較長,所以內部做了一個進度估計。
待 階段1 完成初始化,執行體的各個元件都進入了一個正常狀態。但只有核心是沒有意義的,系統必須讓應用程式跑起來。應用程式的啟動就得依賴剛啟動的 smss 程式繼續了。
應用初始化
現在控制權來到了 smss,smss 是 Windows 中一個重要的程式。
smss 還會繼續完成引導過程。在核心初始化期間,只有 System 儲巢被載入到記憶體當中了。其他的就得由 smss 來載入。其他初始化必要的工作,可以參看 HKLM\SYSTEM\CurrentControlSet\Control\Session Manager 。
登錄檔中能發現一些有趣的子鍵。
- 建立系統的全域性環境變數,這些環境變數由 Environment 鍵下的值指定
- 執行在啟動時執行的程式,這些程式由 BootExecute 值指定
- 執行啟動時的檔案刪除或重新命名任務,這由 FileRenameOperations 子鍵來指定
- 啟動 Windows 子系統程式(csrss.exe)。子系統程式的命令列字串由 SubSystems 子鍵的 Windows 值指定
然後 smss 便會啟動 winlogon 程式,隨後接下來的引導便交由 winlogon 程式了。 winlogon 主要職責如下:
- 建立初始視窗
- 建立登入桌面和預設桌面
- 啟動服務控制管理器(SCM,Service Control Manager)程式(services.exe)
- 啟動本地安全權威子系統(lsass)程式
在登入過程的最後,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)已經啟動,因此使用者可以在桌面上操作了。
至此,引導就全部結束了。