KiInitializeKernel 函数分析
KiInitializeKernel 函数分析1. 概述KiInitializeKernel是KiSystemStartupBootStack调用的核心初始化函数负责初始化内核执行体Kernel Executive的所有子系统。它设置 NX (DEP) 策略、初始化空闲进程和线程、注册 CPU 特性到共享用户数据区然后调用ExpInitializeExecutive完成各子系统HAL、对象管理器、安全、进程/线程、配置管理器等的初始化最后通过设置空闲线程优先级为 0 触发调度器切换到 Phase1 初始化线程。函数签名:CODE_SEG(INIT)VOID NTAPIKiInitializeKernel(IN PKPROCESS InitProcess,IN PKTHREAD InitThread,IN PVOID IdleStack,IN PKPRCB Prcb,IN CCHAR Number,IN PLOADER_PARAMETER_BLOCK LoaderBlock)文件位置: [ntoskrnl/ke/i386/kiinit.c#L433-L638](file:///d:/reactos/ntoskrnl/ke/i386/kiinit.c#L433)2. 参数说明参数类型来源含义InitProcessPKPROCESSKiInitialProcess.Pcb初始进程的 PCB系统根进程InitThreadPKTHREADKeLoaderBlock-Thread初始空闲线程 (KiInitialThread)IdleStackPVOIDKeLoaderBlock-KernelStack ~3空闲线程的内核栈 (P0BootStack)PrcbPKPRCBPCR-Prcb通过 FS 读取当前 CPU 的 PRCBNumberCCHARKeNumberProcessors - 1当前 CPU 编号LoaderBlockPLOADER_PARAMETER_BLOCKKeLoaderBlock引导加载程序参数块3. 执行流程图┌──────────────────────────────────────────────────────────────────────┐ │ KiInitializeKernel │ │ │ │ InitProcess, InitThread, IdleStack, Prcb, Number, LoaderBlock │ └────────────────────────────────┬──────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────┐ │ 1. PoInitializePrcb(Prcb) │ │ └─ 初始化电源管理 (PRCB 级别) │ └─────────────────────────────────┘ │ ▼ ┌─────────────────────────────────┐ │ 2. 设置 KeFeatureBits │ │ (仅 BSP: Number0) │ └─────────────────────────────────┘ │ ▼ ┌─────────────────────────────────┐ │ 3. 解析 NX (DEP) 策略 │ │ 从 LoadOptions 读取: │ │ NOEXECUTEALWAYSON → 始终开启 │ │ NOEXECUTEOPTOUT → 选择退出 │ │ NOEXECUTEOPTIN → 选择加入 │ │ NOEXECUTE → 开启 │ │ ALWAYSOFF / EXECUTE → 禁用 │ └──────────────┬──────────────────────┘ │ ▼ ┌─────────────────────────────────┐ │ 4. KiSaveProcessorControlState │ │ └─ 保存当前 CPU 寄存器状态 │ └─────────────────────────────────┘ │ ▼ ┌─────────────────────────────────┐ │ 5. KiGetCacheInformation() │ │ └─ 获取 CPU 缓存行信息 │ └─────────────────────────────────┘ │ ▼ ┌─────────────────────────────────┐ │ 6. KiInitSpinLocks(Prcb, Number) │ │ └─ 初始化自旋锁和 DPC 数据 │ └─────────────────────────────────┘ │ ▼ ┌─────────────────────────────────┐ │ 7. 设置 PRCB-ParentNode │ │ └─ 关联到 NUMA 节点 0 │ └─────────────────────────────────┘ │ ▼ ┌─────────────────────────────────┐ │ 8. BSP (Number0) ? │ └────────┬───────────────┬─────────┘ │ │ ▼ ▼ ┌────────────────────┐ ┌──────────────────┐ │ BSP 路径: │ │ AP 路径: │ │ │ │ │ │ a. 设置全局 CPU │ │ DPRINT1(调试消息) │ │ 类型变量 │ │ │ │ b. KeLowerIrql( │ └──────────────────┘ │ APC_LEVEL) │ │ c. 初始化自旋锁 │ │ d. KiInitSystem() │ │ └─ 初始化便携 │ │ 部分 │ │ e. KeInitializeProc│ │ ess(InitProcess)│ │ └─ 初始化初始 │ │ 进程对象 │ └────────────────────┘ │ ▼ ┌─────────────────────────────────┐ │ 9. KeInitializeThread() │ │ 初始化空闲线程对象 │ │ └─ InitThread 使用 IdleStack │ └─────────────────────────────────┘ │ ▼ ┌─────────────────────────────────┐ │ 10. 设置空闲线程字段: │ │ NextProcessor Number │ │ Priority HIGH_PRIORITY │ │ State Running │ │ Affinity 1 Number │ │ WaitIrql DISPATCH_LEVEL │ └─────────────────────────────────┘ │ ▼ ┌─────────────────────────────────┐ │ 11. 设置 SharedUserData │ │ 处理器特性位 │ │ (MMX, SSE, RDTSC, 3DNow!...) │ └─────────────────────────────────┘ │ ▼ ┌─────────────────────────────────┐ │ 12. 设置 PRCB 线程字段: │ │ CurrentThread InitThread │ │ NextThread NULL │ │ IdleThread InitThread │ └─────────────────────────────────┘ │ ▼ ┌───────────────────────────────────────────────────┐ │ ★ 13. ExpInitializeExecutive(Number, LoaderBlock) ★ │ │ └─ 初始化所有执行体子系统 │ │ HalInitSystem(0, ...) HAL Phase 0 │ │ InbvInitSystem() 启动视频 │ │ IoInitSystem(PreDrivers) I/O (预驱动) │ │ ObInitSystem() 对象管理器 │ │ SeInitSystem() 安全引用监视器 │ │ PsInitSystem(LoaderBlock) 进程/线程管理器 │ │ └─ 创建 Phase1Initialization 线程 │ │ CmInitSystem() 配置管理器/注册表 │ │ ... │ └───────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────┐ │ 14. BSP (Number0) ? │ └────────┬───────────────┬─────────┘ │ │ ▼ ▼ ┌────────────────────┐ ┌──────────────────┐ │ BSP 专属后处理: │ │ 跳过 │ │ │ │ │ │ a. 计算时间倒数 │ └──────────────────┘ │ b. 更新 DPC 值 │ │ c. 分配 DPC 栈 │ │ d. 分配 IOPM 区域 │ └────────────────────┘ │ ▼ ┌─────────────────────────────────┐ │ 15. KeRaiseIrql(DISPATCH_LEVEL) │ └─────────────────────────────────┘ │ ▼ ┌──────────────────────────────────────────────┐ │ ★ 16. KeSetPriorityThread(InitThread, 0) ★ │ │ └─ 将空闲线程优先级设为 0 │ │ → 触发调度器上下文切换 │ │ → Phase1Initialization 线程获得 CPU │ └──────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────┐ │ 17. 空闲摘要检查 │ │ if (!Prcb-NextThread) │ │ KiIdleSummary | (1Num) │ └─────────────────────────────────┘ │ ▼ ┌─────────────────────────────────┐ │ 18. KeRaiseIrql(HIGH_LEVEL) │ │ LoaderBlock-Prcb 0 │ │ └─ 返回 KiSystemStartupBootStack │ └─────────────────────────────────┘ │ ▼ [返回]4. 函数执行详解4.1 电源管理初始化PoInitializePrcb(Prcb);初始化该 CPU 的 PRCB 中的电源管理数据。PRCB 包含PowerState等字段用于跟踪处理器的电源状态如 C 状态、节流级别等。4.2 设置 CPU 特性位 (BSP)if(Number0)KeFeatureBitsPrcb-FeatureBits|(ULONG64)Prcb-FeatureBitsHigh32;将硬件检测到的 CPU 特性位从 PRCB 复制到全局变量KeFeatureBits。这是内核其他部分如内存管理器、调度器查询 CPU 能力的统一入口。KeFeatureBits包含的常见特性位位标志含义KF_FXSRFXSAVE/FXRSTOR 指令 (SSE)KF_MMXMMX 指令KF_XMMISSE 指令KF_XMMI64SSE2 指令KF_RDTSCRDTSC 指令KF_CMPXCHG8BCMPXCHG8B 指令KF_LARGE_PAGE大页 (4MB) 支持KF_GLOBAL_PAGE全局页 (PGE) 支持KF_PATPage Attribute TableKF_MTRRMemory Type Range RegistersKF_3DNOW3DNow! 指令 (AMD)4.3 NX (DEP) 策略解析if(strstr(KeLoaderBlock-LoadOptions,NOEXECUTEALWAYSON)){SharedUserData-NXSupportPolicyNX_SUPPORT_POLICY_ALWAYSON;KeFeatureBits|KF_NX_ENABLED;}// ... 其他选项从引导选项解析 NX (No-eXecute) / DEP (Data Execution Prevention) 策略。策略选项优先级和含义选项NXSupportPolicy效果NOEXECUTEALWAYSONALWAYSON始终开启无法关闭NOEXECUTEOPTOUTOPTOUT默认开启但应用程序可选择性关闭NOEXECUTEOPTIN(默认)OPTIN默认关闭仅标记为兼容的程序开启NOEXECUTEOPTIN(不修改)仅设置KF_NX_ENABLEDNOEXECUTEALWAYSOFF/EXECUTEALWAYSOFF始终关闭4.4 保存处理器状态KiSaveProcessorControlState(Prcb-ProcessorState);将当前 CPU 的完整控制寄存器状态保存到 PRCB 中包括CR0, CR2, CR3, CR4GDTR, IDTR, TR, LDTRCR8 (x64 only) / APIC 相关4.5 缓存行信息KiGetCacheInformation();检测当前 CPU 的缓存层次结构L1/L2/L3 大小、缓存行长度存储到全局变量供内存管理器和调度器使用。4.6 自旋锁和 DPC 初始化KiInitSpinLocks(Prcb,Number);初始化 PRCB 中的自旋锁和 DPC (Deferred Procedure Call) 数据结构包括 DPC 队列、DPC 请求位掩码等。4.7 NUMA 节点关联Prcb-ParentNodeKeNodeBlock[0];Prcb-ParentNode-ProcessorMask|Prcb-SetMember;将该 CPU 关联到 NUMA 节点 0非一致性内存访问系统中的根节点。在单插槽系统上始终使用节点 0。4.8 BSP 专属初始化 (Number 0)if(!Number){// a. 设置全局 CPU 类型变量KeI386CpuTypePrcb-CpuType;KeI386CpuStepPrcb-CpuStep;KeProcessorArchitecturePROCESSOR_ARCHITECTURE_INTEL;KeProcessorLevel(USHORT)Prcb-CpuType;if(Prcb-CpuID)KeProcessorRevisionPrcb-CpuStep;KeI386FxsrPresent(KeFeatureBitsKF_FXSR)?TRUE:FALSE;KeI386XMMIPresent(KeFeatureBitsKF_XMMI)?TRUE:FALSE;// b. 设置 MP Master KPRCBPrcb-MultiThreadSetMasterPrcb;// c. 降低 IRQL 到 APC_LEVELKeLowerIrql(APC_LEVEL);// d. 初始化全局自旋锁KeInitializeSpinLock(KiFreezeExecutionLock);KeInitializeSpinLock(Ki486CompatibilityLock);// e. ★ 初始化可移植的内核部分 ★KiInitSystem();// f. 初始化进程链表和 KiInitialProcessInitializeListHead(KiProcessListHead);KeInitializeProcess(InitProcess,0,MAXULONG_PTR,PageDirectory,FALSE);InitProcess-QuantumResetMAXCHAR;}KiInitSystem()初始化内核中与架构无关的可移植部分包括内核调度器全局状态默认量子值DPC 阈值参数定时器相关结构各种全局列表头KeInitializeProcess(InitProcess, ...)将静态分配的KiInitialProcess初始化为可用的 KPROCESS 对象进程基优先级: 0最大进程亲和性:MAXULONG_PTR(所有 CPU)页目录数组: 空由内存管理器后续填充4.9 初始化空闲线程KeInitializeThread(InitProcess,InitThread,NULL,NULL,NULL,NULL,NULL,IdleStack);将KiInitialThread初始化为可用的 KTHREAD 对象关联到KiInitialProcess内核栈为IdleStack(P0BootStack)初始上下文为空空闲线程不需要用户态上下文InitThread-NextProcessorNumber;InitThread-PriorityHIGH_PRIORITY;// 高优先级(13)防止被其他线程抢占InitThread-StateRunning;// 标记为运行中InitThread-Affinity1Number;// 绑定到当前 CPUInitThread-WaitIrqlDISPATCH_LEVEL;// 等待 IRQLInitProcess-ActiveProcessors|1Number;// 标记在线 CPU注意此处Priority HIGH_PRIORITY是临时值目的是在ExpInitializeExecutive执行期间不让调度器切换走空闲线程。稍后KeSetPriorityThread(InitThread, 0)会将优先级降为 0。4.10 设置 SharedUserData 特性位SharedUserData-ProcessorFeatures[PF_MMX_INSTRUCTIONS_AVAILABLE]...;SharedUserData-ProcessorFeatures[PF_COMPARE_EXCHANGE_DOUBLE]...;// ... 其他特性向用户模式可读的共享数据区 (0x7FFE0000) 写入 CPU 特性。用户模式代码如应用程序和 DLL通过读取此区域了解 CPU 支持哪些指令集而无需执行CPUID指令。4.11 设置 PRCB 线程字段Prcb-CurrentThreadInitThread;Prcb-NextThreadNULL;Prcb-IdleThreadInitThread;CurrentThread— PRCB 中标识当前正在执行的线程NextThread— 下一个准备切换的线程初始为 NULLIdleThread— 空闲线程指针调度器在空闲时返回此线程4.12 ★ ExpInitializeExecutive — 执行体子系统初始化ExpInitializeExecutive(Number, LoaderBlock) │ ├─ Phase 0 初始化 (在 Idle 线程上运行): │ ├─ HalInitSystem(0, LoaderBlock) │ │ └─ HAL 初始化 Phase 0: HAL 堆、HAL 堆、HAL 调试资源 │ │ │ ├─ InbvInitSystem() │ │ └─ 初始化启动视频显示 (BootVid) │ │ │ ├─ IoInitSystem(LoaderBlock) │ │ └─ I/O 系统预初始化 (IoInitSystemPreDrivers) │ │ │ ├─ ObInitSystem() │ │ └─ 对象管理器初始化: 创建对象类型、命名空间 │ │ │ ├─ SeInitSystem() │ │ └─ 安全引用监视器初始化 │ │ │ ├─ PsInitSystem(LoaderBlock) │ │ └─ 进程/线程管理器初始化 │ │ ├─ 初始化系统进程 System (PID 4) │ │ ├─ 创建 Phase1Initialization 线程 (Priority 13) │ │ └─ 注册初始进程和线程 │ │ │ ├─ CmInitSystem() │ │ └─ 配置管理器/注册表初始化 │ │ └─ 加载 SYSTEM 配置单元 │ │ │ ├─ RtlMultiByteToUnicodeSize() 等 RTL 初始化 │ └─ ... │ └─ [返回 KiInitializeKernel]Phase1Initialization 线程— 这是系统初始化过程中最关键的子线程。它在PsInitSystem中被创建优先级为 13高于空闲线程。当空闲线程优先级被设为 0 后调度器将切换到该线程执行Phase1Initialization() ├─ HalInitSystem(1, LoaderBlock) ← HAL Phase 1 ├─ KdInitSystem(1, LoaderBlock) ← KD Phase 1 ├─ IoInitSystem(LoaderBlock) ← I/O Phase 1 (驱动程序加载) ├─ ... └─ 最终: 创建 SMSS → 子系统初始化4.13 BSP 专属后处理if(!Number){KiTimeIncrementReciprocalKiComputeReciprocal(KeMaximumIncrement,...);Prcb-MaximumDpcQueueDepthKiMaximumDpcQueueDepth;Prcb-MinimumDpcRateKiMinimumDpcRate;Prcb-AdjustDpcThresholdKiAdjustDpcThreshold;DpcStackMmCreateKernelStack(FALSE,0);if(!DpcStack)KeBugCheckEx(NO_PAGES_AVAILABLE,1,0,0,0);Prcb-DpcStackDpcStack;Ki386IopmSaveAreaExAllocatePoolWithTag(PagedPool,IOPM_SIZE,TAG_KERNEL);if(!Ki386IopmSaveArea)KeBugCheckEx(NO_PAGES_AVAILABLE,2,IOPM_SIZE,0,0);}计算时间倒数—KiComputeReciprocal计算时钟增量的倒数用于高效地将滴答计数转换为微秒更新 DPC 值— 可能在ExpInitializeExecutive中被KiInitSystem或调试器修改现在同步回 PRCB分配 DPC 栈—MmCreateKernelStack为 DPC 处理分配独立的内核栈防止 DPC 在空闲线程栈上运行导致栈溢出分配 IOPM 保存区— I/O 权限位图 (IOPM) 用于 V86 模式下的端口 I/O 仿真VDM (Virtual DOS Machine) 支持必需4.14 触发调度器KeRaiseIrql(DISPATCH_LEVEL,DummyIrql);KeSetPriorityThread(InitThread,0);这是整个引导过程的转折点将 IRQL 提升到DISPATCH_LEVEL禁止线程调度中断KeSetPriorityThread(InitThread, 0)将空闲线程优先级从HIGH_PRIORITY降为 0调度器在设置优先级时检查是否有更高优先级的线程就绪由于Phase1Initialization线程优先级为 13 0调度器执行上下文切换4.15 返回前的清理KiAcquirePrcbLock(Prcb);if(!Prcb-NextThread)KiIdleSummary|1Number;KiReleasePrcbLock(Prcb);KeRaiseIrql(HIGH_LEVEL,DummyIrql);LoaderBlock-Prcb0;如果Prcb-NextThread为 NULL没有就绪线程将该 CPU 标记为空闲可用将 IRQL 再次提升到HIGH_LEVEL为KiSwitchToBootStack后的空闲循环做准备LoaderBlock-Prcb 0— 告知引导加载程序不再需要 PRCB 数据5. 调用链总结KiSystemStartupBootStack └─ KiInitializeKernel(InitProcess, InitThread, IdleStack, Prcb, 0, LoaderBlock) │ ├─ [前置初始化] │ ├─ PoInitializePrcb(Prcb) — 电源管理 │ ├─ 设置 KeFeatureBits — CPU 特性 │ ├─ 解析 NX/DEP 策略 — 内存保护 │ ├─ KiSaveProcessorControlState — 保存 CPU 状态 │ ├─ KiGetCacheInformation() — 缓存信息 │ ├─ KiInitSpinLocks(Prcb, Number) — 自旋锁 DPC │ └─ 设置 PRCB-ParentNode — NUMA 节点 │ ├─ [BSP 专属: KiInitSystem 和初始化进程] │ ├─ 设置全局 CPU 类型变量 │ ├─ KiInitSystem() — 可移植内核初始化 │ └─ KeInitializeProcess(InitProcess) — 初始化系统进程 │ ├─ [通用: 初始化空闲线程] │ ├─ KeInitializeThread(InitThread, IdleStack) — 初始化空闲线程 │ ├─ 设置线程字段 (Priority, State, Affinity) │ └─ 填充 SharedUserData 特性位 │ ├─ [通用: 设置 PRCB] │ └─ CurrentThread InitThread │ IdleThread InitThread │ ├─ ★ ExpInitializeExecutive(Number, LoaderBlock) ★ │ ├─ HalInitSystem(0, LoaderBlock) HAL Phase 0 │ ├─ ObInitSystem() 对象管理器 │ ├─ SeInitSystem() 安全引用监视器 │ ├─ PsInitSystem(LoaderBlock) 进程管理器 │ │ └─ 创建 Phase1Initialization 线程 │ ├─ CmInitSystem() 配置管理器 │ └─ ... │ ├─ [BSP 专属后处理] │ ├─ 计算时间倒数 │ ├─ 分配 DPC 栈 │ └─ 分配 IOPM 保存区 │ └─ ★ KeSetPriorityThread(InitThread, 0) ★ — 触发调度器切换 └─ [切换到 Phase1Initialization 线程] └─ HalInitSystem(1) → IoInitSystem() → SMSS → ...6. 设计要点6.1 BSP/AP 复用设计此函数同时处理 BSP 和 AP 的初始化但 BSP 执行的工作量显著多于 AP步骤BSP (Number0)AP (Number0)PoInitializePrcb✓✓KeFeatureBits设置✓✗NX 策略解析✓✓保存 CPU 状态✓✓缓存信息✓✓KiInitSpinLocks✓✓KiInitSystem(可移植内核)✓✗初始化进程✓✗KeInitializeThread✓✓ExpInitializeExecutive✓✓ (仅 Phase 0 公共部分)分配 DPC 栈✓✗KeSetPriorityThread(..., 0)✓✓6.2 Phase 0 和 Phase 1 的分段初始化执行体初始化分为两个阶段Phase 0(在空闲线程上运行)建立内核的核心框架——对象管理器、安全引用监视器、进程管理器、配置管理器等。这些子系统不需要完整的 I/O 栈即可运行。Phase 1(在 Phase1Initialization 线程上运行)初始化完整的 I/O 系统、加载启动驱动、初始化调试器。这些操作必须在一个真正的线程上下文中进行以便可以等待 I/O 完成。6.3 空闲线程优先级技巧空闲线程初始优先级为HIGH_PRIORITY然后在ExpInitializeExecutive完成后降为 0HIGH_PRIORITY— 防止在 ExpInitializeExecutive 期间发生调度切换此时内核尚不完整Priority 0— 降为最低优先级后调度器立即切换到 Phase1Initialization 线程优先级 136.4 共享用户数据 (SharedUserData)SharedUserData位于虚拟地址0x7FFE0000通过页表映射为只读用户模式可访问其中包含NXSupportPolicy— DEP 策略用户模式程序据此决定是否启用 DEPProcessorFeatures[]— CPU 特性位数组用户模式通过IsProcessorFeaturePresent()API 读取6.5 DPC 栈分配DPC (Deferred Procedure Call) 使用独立的 DPC 栈而非当前线程栈。这是因为 DPC 可以在任何 IRQL DISPATCH_LEVEL 的上下文中运行且 DPC 处理可能需要较大栈空间。MmCreateKernelStack分配一个新的内核栈并保存到Prcb-DpcStack。