reactos操作系统实现(17)

本文分析了操作系统内核初始化过程中关键函数KiInitializeKernel的工作原理,详细解释了如何通过KiSetProcessorType函数检测CPU类型及特性,并将其保存到PRCB控制块中。此外,还介绍了如何利用CPUID指令获取CPU的具体信息。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

从上面分析可以看到,主要调用函数KiInitializeKernel来初始化内核,这个函数传送的参数与函数KiSetupStackAndInitializeKernel的参数是一样的,如下:

#001 VOID

#002 NTAPI

#003 KiInitializeKernel(IN PKPROCESS InitProcess,

#<chmetcnv tcsc="0" numbertype="1" negative="False" hasspace="True" sourcevalue="4" unitname="in" w:st="on">004<span style="mso-spacerun:yes"> </span>IN</chmetcnv> PKTHREAD InitThread,

#<chmetcnv tcsc="0" numbertype="1" negative="False" hasspace="True" sourcevalue="5" unitname="in" w:st="on">005<span style="mso-spacerun:yes"> </span><span style="mso-spacerun:yes"></span>IN</chmetcnv> PVOID IdleStack,

#<chmetcnv tcsc="0" numbertype="1" negative="False" hasspace="True" sourcevalue="6" unitname="in" w:st="on">006<span style="mso-spacerun:yes"> </span>IN</chmetcnv> PKPRCB Prcb,

#<chmetcnv tcsc="0" numbertype="1" negative="False" hasspace="True" sourcevalue="7" unitname="in" w:st="on">007<span style="mso-spacerun:yes"> </span>IN</chmetcnv> CCHAR Number,

#<chmetcnv tcsc="0" numbertype="1" negative="False" hasspace="True" sourcevalue="8" unitname="in" w:st="on">008<span style="mso-spacerun:yes"> </span>IN</chmetcnv> PLOADER_PARAMETER_BLOCK LoaderBlock)

#009 {

上面传递的参数都是基于栈式,因此在这里就可以取到在汇编代码压栈参数。

#010 BOOLEAN NpxPresent;

#011 ULONG FeatureBits;

#012 ULONG PageDirectory[2];

#013 PVOID DpcStack;

#014 ULONG Vendor[3];

#015

#016 /* Detect and set the CPU Type */

#017 KiSetProcessorType();

上面调用函数KiSetProcessorType来检测当前硬件系统里的CPU类型,并且把CPU类型和特性保存到控制块PRCB里。因为不同的CPU具有不同的功能,比如INTELAMDVIA都是X86CPU,但它们是有很多浮点数指令不一样的,这样就需要针对不同的CPU作不同的处理。现在就来分析这个函数的实现,它的代码如下:

#001 VOID

#002 NTAPI

#003 KiSetProcessorType(VOID)

#004 {

#005 ULONG EFlags = 0, NewEFlags;

#006 ULONG Reg[4];

#007 ULONG Stepping, Type;

#008

#009 /* Start by assuming no CPUID data */

#010 KeGetCurrentPrcb()->CpuID = 0;

设置CPUID指令是否支持,这里初始化为不支持。

#011

#012 /* Save EFlags */

#013 Ke386SaveFlags(EFlags);

保存CPU当前的标志位,因为后面需要检测CPUID

#014

#015 /* XOR out the ID bit and update EFlags */

#016 NewEFlags = EFlags ^ EFLAGS_ID;

#017 Ke386RestoreFlags(NewEFlags);

CPUID指令位清空,再把它设置回到标志寄存器。

#018

#019 /* Get them back and see if they were modified */

#020 Ke386SaveFlags(NewEFlags);

获取标志寄存器的值,下面查看是否会改变这个值。

#021 if (NewEFlags != EFlags)

#022 {

如果标志位有变化,说明就是支持CPUID的指令。

#023 /* The modification worked, so CPUID exists. Set the ID Bit again. */

#024 EFlags |= EFLAGS_ID;

#025 Ke386RestoreFlags(EFlags);

#026

#027 /* Peform CPUID 0 to see if CPUID 1 is supported */

#028 CPUID(Reg, 0);

先操作CPUID0类型的指令。

#029 if (Reg[0] > 0)

#030 {

#031 /* Do CPUID 1 now */

#032 CPUID(Reg, 1);

操作CPUID1类型的指令。

#033

#034 /*

#035 * Get the Stepping and Type. The stepping contains both the

#036 * Model and the Step, while the Type contains the returned Type.

#037 * We ignore the family.

#038 *

#039 * For the stepping, we convert this: zzzzzzxy into this: x0y

#040 */

#041 Stepping = Reg[0] & 0xF0;

#042 Stepping <<= 4;

#043 Stepping += (Reg[0] & 0xFF);

#044 Stepping &= 0xF<chmetcnv unitname="F" sourcevalue="0" hasspace="False" negative="False" numbertype="1" tcsc="0" w:st="on">0F</chmetcnv>;

#045 Type = Reg[0] & 0xF00;

#046 Type >>= 8;

#047

#048 /* Save them in the PRCB */

#049 KeGetCurrentPrcb()->CpuID = TRUE;

#050 KeGetCurrentPrcb()->CpuType = (UCHAR)Type;

#051 KeGetCurrentPrcb()->CpuStep = (USHORT)Stepping;

上面获取到了CPU的类型,步进信息。

#052 }

#053 else

#054 {

#055 DPRINT1("CPUID Support lacking/n");

#056 }

#057 }

#058 else

#059 {

#060 DPRINT1("CPUID Support lacking/n");

#061 }

#062

#063 /* Restore EFLAGS */

#064 Ke386RestoreFlags(EFlags);

恢复原来标志寄存器值。

#065 }

其实这段代码是获取CPU类型和步进信息,不同的CPU获取的信息是不一样,下面通过其它软件获取到我的CPU信息,如下图:


在这个CPU应就是获取到CPU类型为6,而步进为FB。至于其它CPU信息,还会在后面继续探测和设置到内核数据结构里。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值