visual-c++ - 如何用C/C++通过CPUID命令获取物理和虚拟地址位

标签 visual-c++ x86 operating-system bit cpuid

我通过在 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/

相关文章:

c++ - COleControl的ActiveX和HWND

c++ - testb $1, %al 的语义是什么

linux - Linux 内核崩溃消息中的 "Code"是什么?

process - 硬件与软件在上下文切换中的作用

windows - 为什么Createprocess返回NULL?

c++ - 为什么从 unsigned long long 转换为 double 会导致数据丢失?

c++ - DirectX 碰撞测试。 - 自己造引擎值得吗?

c++ - 更改对话框的图标

c - 汇编中保留的堆栈空间似乎与 C 代码不匹配

operating-system - 虚拟内存空间