如何通过 CPUID 命令使用 C/C++ 获取物理和虚拟地址位
How to get physical and virtual address bits with C/C++ by CPUID command
我在 windows 中使用 CPUID 命令通过 C 获取物理和虚拟地址位大小。
我可以通过这种方式获取处理器信息,但我对获取地址位感到困惑。
看起来我应该给你 80000008 指令,但我这样做,只显示连续变化的 7-8 位数字。
我想了解这个命令的工作原理并解决这个问题
#include <stdio.h>
void getcpuid(int T, int* val) {
int reg_ax;
int reg_bx;
int reg_cx;
int reg_dx;
__asm {
mov eax, T;
cpuid;
mov reg_ax, eax;
mov reg_bx, ebx;
mov reg_cx, ecx;
mov reg_dx, edx;
}
*(val + 0) = reg_ax;
*(val + 1) = reg_bx;
*(val + 2) = reg_cx;
*(val + 3) = reg_dx;
}
int main() {
int val[5]; val[4] = 0;
getcpuid(0x80000002, val);
printf("%s\r\n", &val[0]);
getcpuid(0x80000003, val);
printf("%s\r\n", &val[0]);
getcpuid(0x80000004, val);
printf("%s\r\n", &val[0]);
return 0;
}
将EAX = 80000002, 80000003, 80000004 运行此代码时,显示英特尔处理器品牌字符串。
我把 80000008 用于获取物理和虚拟地址位,但显示的随机数不断变化。
我想知道如何使用带有 80000008 的 cpuid commend 来获取这些地址位
我是编程和操作系统初学者。
请让我知道我必须做什么。
您使用的内联汇编可能是正确的;但这取决于它是哪个编译器。我认为它适用于 Microsoft 的 MSVC(但我从未使用过它,也不能确定)。对于 GCC(和 CLANG),您必须通知编译器您正在修改寄存器和内存的内容(通过 clobber 列表),并且告诉编译器您正在输出 4 个值会更有效在 4 个寄存器中。
主要问题是您试图将输出视为(以空字符结尾的)字符串;由 CPUID 编辑的数据 return 永远不会是空终止字符串(即使对于“获取供应商字符串”和“获取品牌名称字符串”,它也是一个没有零终止符的空格填充字符串)。
要解决这个问题,您可以:
void getcpuid(int T, int* val) {
unsigned int reg_ax;
unsigned int reg_bx;
unsigned int reg_cx;
unsigned int reg_dx;
__asm {
mov eax, T;
cpuid;
mov reg_ax, eax;
mov reg_bx, ebx;
mov reg_cx, ecx;
mov reg_dx, edx;
}
*(val + 0) = reg_ax;
*(val + 1) = reg_bx;
*(val + 2) = reg_cx;
*(val + 3) = reg_dx;
}
int main() {
uint32_t val[5]; val[4] = 0;
getcpuid(0x80000002U, val);
printf("0x%08X\r\n", val[0]);
getcpuid(0x80000003U, val);
printf("0x%08X\r\n", val[1]);
getcpuid(0x80000004U, val);
printf("0x%08X\r\n", val[2]);
return 0;
}
下一个问题是提取虚拟地址大小和物理地址大小值。这些是打包到 eax
的第一个和第二个字节中的 8 位值;所以:
int main() {
uint32_t val[5]; val[4] = 0;
int physicalAddressSize;
int virtualAddressSize;
getcpuid(0x80000008U, val);
physicalAddressSize = val[0] & 0xFF;
virtualAddressSize= (val[0] >> 8) & 0xFF;
printf("Virtual %d, physical %d\r\n", virtualAddressSize, physicalAddressSize);
return 0;
}
这应该适用于最近的 CPU;这意味着它在较旧的 CPU 上仍然很糟糕并且损坏。
要开始修复您想要检查 CPU 是否支持“CPUID leaf 0x80000008”,然后再假设它存在:
int main() {
uint32_t val[5]; val[4] = 0;
int physicalAddressSize;
int virtualAddressSize;
getcpuid(0x80000000U, val);
if(val(0) < 0x80000008U) {
physicalAddressSize = -1;
virtualAddressSize = -1;
} else {
getcpuid(0x80000008U, val);
physicalAddressSize = val[0] & 0xFF;
virtualAddressSize= (val[0] >> 8) & 0xFF;
}
printf("Virtual %d, physical %d\r\n", virtualAddressSize, physicalAddressSize);
return 0;
}
当“CPUID 叶子 0x80000008”不存在时,您可以 return 更正结果。对于所有不支持“CPUID leaf 0x80000008”的CPU;虚拟地址大小为 32 位,物理地址大小为 36 位(如果支持 PAE)或 32 位(如果不支持 PAE)。可以通过CPUID判断CPU是否支持PAE,所以最后有点像这样:
int main() {
uint32_t val[5]; val[4] = 0;
int physicalAddressSize;
int virtualAddressSize;
getcpuid(0x80000000U, val);
if(val(0) < 0x80000008U) {
getcpuid(0x00000000U, val);
if(val[0] == 0) {
physicalAddressSize = 32; // "CPUID leaf 0x00000001" not supported
} else {
getcpuid(0x00000001U, val);
if( val[3] & (1 << 6) != 0) {
physicalAddressSize = 36; // PAE is supported
} else {
physicalAddressSize = 32; // PAE not supported
}
}
virtualAddressSize = 32;
} else {
getcpuid(0x80000008U, val);
physicalAddressSize = val[0] & 0xFF;
virtualAddressSize= (val[0] >> 8) & 0xFF;
}
printf("Virtual %d, physical %d\r\n", virtualAddressSize, physicalAddressSize);
return 0;
}
另一个问题是有时 CPUID 有问题;这意味着您必须为每个 CPU(来自 Intel、AMD、VIA 等)遍历每个勘误表 sheet,以确保 CPUID 的结果实际上是正确的。例如,“Intel Pentium 4 Processor on 90 nm Process”有3个型号,其中“CPUID leaf 0x800000008”是错误的,说“物理地址是40位”,但实际上是36位。
对于所有这些情况,您需要实施 work-arounds(例如,从 CPUID 获取 CPU vendor/family/model/stepping 信息,如果它与 3 个越野车之一相匹配Pentium 4 型号,执行“if(physicalAddressSize == 40) physicalAddressSize = 36;
”以修复 CPU 的错误。
我在 windows 中使用 CPUID 命令通过 C 获取物理和虚拟地址位大小。 我可以通过这种方式获取处理器信息,但我对获取地址位感到困惑。 看起来我应该给你 80000008 指令,但我这样做,只显示连续变化的 7-8 位数字。 我想了解这个命令的工作原理并解决这个问题
#include <stdio.h>
void getcpuid(int T, int* val) {
int reg_ax;
int reg_bx;
int reg_cx;
int reg_dx;
__asm {
mov eax, T;
cpuid;
mov reg_ax, eax;
mov reg_bx, ebx;
mov reg_cx, ecx;
mov reg_dx, edx;
}
*(val + 0) = reg_ax;
*(val + 1) = reg_bx;
*(val + 2) = reg_cx;
*(val + 3) = reg_dx;
}
int main() {
int val[5]; val[4] = 0;
getcpuid(0x80000002, val);
printf("%s\r\n", &val[0]);
getcpuid(0x80000003, val);
printf("%s\r\n", &val[0]);
getcpuid(0x80000004, val);
printf("%s\r\n", &val[0]);
return 0;
}
将EAX = 80000002, 80000003, 80000004 运行此代码时,显示英特尔处理器品牌字符串。 我把 80000008 用于获取物理和虚拟地址位,但显示的随机数不断变化。 我想知道如何使用带有 80000008 的 cpuid commend 来获取这些地址位
我是编程和操作系统初学者。 请让我知道我必须做什么。
您使用的内联汇编可能是正确的;但这取决于它是哪个编译器。我认为它适用于 Microsoft 的 MSVC(但我从未使用过它,也不能确定)。对于 GCC(和 CLANG),您必须通知编译器您正在修改寄存器和内存的内容(通过 clobber 列表),并且告诉编译器您正在输出 4 个值会更有效在 4 个寄存器中。
主要问题是您试图将输出视为(以空字符结尾的)字符串;由 CPUID 编辑的数据 return 永远不会是空终止字符串(即使对于“获取供应商字符串”和“获取品牌名称字符串”,它也是一个没有零终止符的空格填充字符串)。
要解决这个问题,您可以:
void getcpuid(int T, int* val) {
unsigned int reg_ax;
unsigned int reg_bx;
unsigned int reg_cx;
unsigned int reg_dx;
__asm {
mov eax, T;
cpuid;
mov reg_ax, eax;
mov reg_bx, ebx;
mov reg_cx, ecx;
mov reg_dx, edx;
}
*(val + 0) = reg_ax;
*(val + 1) = reg_bx;
*(val + 2) = reg_cx;
*(val + 3) = reg_dx;
}
int main() {
uint32_t val[5]; val[4] = 0;
getcpuid(0x80000002U, val);
printf("0x%08X\r\n", val[0]);
getcpuid(0x80000003U, val);
printf("0x%08X\r\n", val[1]);
getcpuid(0x80000004U, val);
printf("0x%08X\r\n", val[2]);
return 0;
}
下一个问题是提取虚拟地址大小和物理地址大小值。这些是打包到 eax
的第一个和第二个字节中的 8 位值;所以:
int main() {
uint32_t val[5]; val[4] = 0;
int physicalAddressSize;
int virtualAddressSize;
getcpuid(0x80000008U, val);
physicalAddressSize = val[0] & 0xFF;
virtualAddressSize= (val[0] >> 8) & 0xFF;
printf("Virtual %d, physical %d\r\n", virtualAddressSize, physicalAddressSize);
return 0;
}
这应该适用于最近的 CPU;这意味着它在较旧的 CPU 上仍然很糟糕并且损坏。
要开始修复您想要检查 CPU 是否支持“CPUID leaf 0x80000008”,然后再假设它存在:
int main() {
uint32_t val[5]; val[4] = 0;
int physicalAddressSize;
int virtualAddressSize;
getcpuid(0x80000000U, val);
if(val(0) < 0x80000008U) {
physicalAddressSize = -1;
virtualAddressSize = -1;
} else {
getcpuid(0x80000008U, val);
physicalAddressSize = val[0] & 0xFF;
virtualAddressSize= (val[0] >> 8) & 0xFF;
}
printf("Virtual %d, physical %d\r\n", virtualAddressSize, physicalAddressSize);
return 0;
}
当“CPUID 叶子 0x80000008”不存在时,您可以 return 更正结果。对于所有不支持“CPUID leaf 0x80000008”的CPU;虚拟地址大小为 32 位,物理地址大小为 36 位(如果支持 PAE)或 32 位(如果不支持 PAE)。可以通过CPUID判断CPU是否支持PAE,所以最后有点像这样:
int main() {
uint32_t val[5]; val[4] = 0;
int physicalAddressSize;
int virtualAddressSize;
getcpuid(0x80000000U, val);
if(val(0) < 0x80000008U) {
getcpuid(0x00000000U, val);
if(val[0] == 0) {
physicalAddressSize = 32; // "CPUID leaf 0x00000001" not supported
} else {
getcpuid(0x00000001U, val);
if( val[3] & (1 << 6) != 0) {
physicalAddressSize = 36; // PAE is supported
} else {
physicalAddressSize = 32; // PAE not supported
}
}
virtualAddressSize = 32;
} else {
getcpuid(0x80000008U, val);
physicalAddressSize = val[0] & 0xFF;
virtualAddressSize= (val[0] >> 8) & 0xFF;
}
printf("Virtual %d, physical %d\r\n", virtualAddressSize, physicalAddressSize);
return 0;
}
另一个问题是有时 CPUID 有问题;这意味着您必须为每个 CPU(来自 Intel、AMD、VIA 等)遍历每个勘误表 sheet,以确保 CPUID 的结果实际上是正确的。例如,“Intel Pentium 4 Processor on 90 nm Process”有3个型号,其中“CPUID leaf 0x800000008”是错误的,说“物理地址是40位”,但实际上是36位。
对于所有这些情况,您需要实施 work-arounds(例如,从 CPUID 获取 CPU vendor/family/model/stepping 信息,如果它与 3 个越野车之一相匹配Pentium 4 型号,执行“if(physicalAddressSize == 40) physicalAddressSize = 36;
”以修复 CPU 的错误。