我通过在 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 运行此代码时,将显示 Intel 处理器品牌字符串。 我输入 80000008 来获取物理和虚拟地址位,但显示的随机数不断变化。 我想知道如何使用这个 cpuid 命令和 80000008 来获取这些地址位
我是编程和操作系统初学者。 请让我知道我必须做什么。
最佳答案
您使用的内联汇编可能是正确的;但这取决于它是哪个编译器。我认为它适合微软的MSVC(但我从未使用过它并且不能确定)。对于 GCC(和 CLANG),您必须通知编译器您正在修改寄存器和内存的内容(通过 clobber 列表),并且告诉编译器您正在输出 4 个值会更有效在 4 个寄存器中。
主要问题是您试图将输出视为(以空字符结尾的)字符串;并且 CPUID 返回的数据永远不是以 null 结尾的字符串(即使对于“获取供应商字符串”和“获取品牌名称字符串”,它也是一个空白填充的字符串,没有零终止符)。
要解决这个问题,您可以:
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 leaf 0x80000008”不存在时,可以返回正确的结果。对于所有不支持“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 等)的每一个勘误表,以确保 CPUID 的结果实际上是正确的。例如,有 3 个型号的“Intel Pentium 4 Processor on 90 nm Process”,其中“CPUID leaf 0x800000008”错误并显示“物理地址是 40 位”,而实际上是 36 位。
对于所有这些情况,您需要实现解决方法(例如,从 CPUID 获取 CPU 供应商/系列/型号/步进信息,如果它与 Pentium 4 的 3 个有缺陷的型号之一匹配,则执行“ if(physicalAddressSize == 40)physicalAddressSize = 36;
”修复CPU的bug)。
关于visual-c++ - 如何用C/C++通过CPUID命令获取物理和虚拟地址位,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64513535/