Windows 啟動過程

2022-06-30 09:00:18

引言

啟動過程是我們瞭解作業系統的第一個環節。瞭解 Windows 的啟動過程,可以幫助我們解決一些啟動的問題,也能幫助我們瞭解 Windows 的整體結構。

以下內容將分為【載入核心】、【核心初始化】和【應用程式初始化】三個部分。

載入核心

啟動過程概覽 所示,載入過程分為兩種方式。傳統的 BIOS(Basic Input/Output System)和 UEFI(Unified Extensible Firmware Interface)。這兩種方式大致相似,本文以 BIOS 為例來說明引導說過。

載入 BIOS

此部分參考:How BIOS Works (yale.edu)

當開機鍵被按下後,電源將執行初始化,確保電源可靠後,主機板上的硬體電路給 CPU 發覆位(reset)訊號。

CPU 收到復位訊號後,將從 RAM 中讀取指令執行。此時記憶體中是沒有任何指令需要執行的。於是 CPU 就去 BIOS ROM 中特定的位置(FFFF0H)中執行指令,這個指令是一個跳轉指令 ,將告訴 CPU 真正的 BIOS 程式所在的位置。跳轉指令如下:

jmp far f000:e05b ; 跳轉到0xfe05b執行

執行 BIOS

BIOS 主要包含以下四個部分的程式:

  • POSTPower 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 的主要任務是:

  • 完成在真真實模式下執行的初始化。比如清除鍵盤緩衝區。
  • 為切換到保護模式做好基本的環境準備。
  • 將處理器切換到保護模式下,然後將控制權交給保護模式下的程式碼。

在保護模式下,需要完成以下步驟:

  1. 由於虛擬機器器制轉譯機制還未就緒,首先要做的就是把實體記憶體管理起來。採用一個記憶體描述符陣列將每一段記憶體的大小和用途記錄下來,然後開啟頁面對映機制
  2. 繼續執行其他的初始化工作。包含 I/O 裝置的初始化等
  3. 從系統分割區的根目錄下讀取 boot.ini 檔案。並檢查是否存在有效的 hiberfil.sys 檔案,如果存在,則將引導過程轉移到休眠系統的恢復過程。否則,將解析 boot.ini 檔案 。
  4. 執行 NTDETECT.COM 程式。此程式執行在真真實模式下,它將利用 BIOS 來查詢系統的基本裝置和設定資訊,並收集起來。在引導後期,將其存放到登入檔 HKLM\HARDWARE\DESCRIPTION 下。
  5. 載入 ntoskrnl.exe、hal(預設為 hal.dll,具體是哪個映像檔案,在 boot.ini 中會有記錄),再通過載入登入檔的 system 儲巢,即 WINDOWS\system32\config\system 檔案 拿到哪些裝置驅動程式必須被載入進來,然後再把必要的裝置驅動載入進來。
  6. 此時載入的準備工作基本就緒,將準備的資訊構造成一個引數塊 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;
  1. 最後,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 主要職責如下:

  1. 建立初始視窗
  2. 建立登入桌面和預設桌面
  3. 啟動服務控制管理器(SCM,Service Control Manager)程序(services.exe)
  4. 啟動本地安全權威子系統(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)已經啟動,因此使用者可以在桌面上操作了。


至此,引導就全部結束了。