当前位置: 首页 > 工具软件 > cpuid > 使用案例 >

使用 CPUID 查询 CPU 信息

符佐
2023-12-01

cpuid 是用来查询 CPU 相关信息的指令。其大致使用方式为:

  1. eax(有时会涉及 ecx)放入指定的值
  2. 使用 cpuid
  3. 在指定寄存器中取值

在 Visual C++ 以及 GCC 中,都有对应的 CPUIDintrin

#if defined(_MSC_VER)
#include <intrin.h>
#elif defined(__GNUC__)
#include <cpuid.h>
#endif // _MSC_VER

利用这些 intrin 我们就无需手写汇编来使用 cpuid。为了方便使用,可以将其封装一下:

void CpuInfo::RunCpuID(std::int32_t eax, std::int32_t ecx, std::int32_t(&abcd)[4]) noexcept
{
    static_assert(std::is_same<std::int32_t, int>::value, "int & int32_t must be the same!");
    std::int32_t* const info = abcd; // decay
#if defined(_MSC_VER)
    __cpuidex(info, eax, ecx);
#elif defined(__GNUC__)
    //http://stackoverflow.com/questions/14266772/how-do-i-call-cpuid-in-linux
    __get_cpuid(eax, info, info + 1, info + 2, info + 3);
#endif // _MSC_VER
}

其中,abcd 是一个 4 个 std::int32_t 的数组,依次存放了执行 cpuideaxebxecxedx 的值。
有了这个函数,剩下的只是查文档了。例如我想要拿到当前 CPU 的具体型号:

void CpuInfo::GetProcessorBrandString(char(&brandString)[49]) noexcept
{   
    std::int32_t info[4] = {};
    const auto infoAsStr = reinterpret_cast<const char*>(info);
    RunCpuID(0x80000002, 0, info);
    auto progress = std::copy(infoAsStr, infoAsStr + 16, brandString);
    RunCpuID(0x80000003, 0, info);
    progress = std::copy(infoAsStr, infoAsStr + 16, progress);
    RunCpuID(0x80000004, 0, info);
    progress = std::copy(infoAsStr, infoAsStr + 16, progress);
    *progress = {};
}

其中,分别把 eax 设置为 0x800000020x800000030x80000004,每次执行 cpuid 后,从 eaxebxecxedx 取出长度为 16 的字符集合(没有空字符结尾)(每次 16 个,3 次共 48 个)。

再例如,我想得到 CPU 的生产厂商。将 eax 置 0 后调用 cpuid,会在 ebxedxecx(注意这个 edxecxinfo 的顺序是反的)放入长度为 12 的字符集合:

CpuInfo::Vendor CpuInfo::GetVendor() noexcept
{
    std::int32_t info[4] = {};
    RunCpuID(0, 0, info);
    //This returns the CPU's manufacturer ID string – a twelve-character ASCII string stored in EBX, EDX, ECX (in that order). 
    const auto searchStart = info + 1;
    //AMD:      "AuthenticAMD"
    //Intel:    "GenuineIntel"
    //VMware:   "VMwareVMware"
    //Hyper-V:  "Microsoft Hv"
    //注意这里:edx 与 ecx 的顺序和 info 中二者的顺序是反的,所以调换后两个寄存器。
    static const char* const VendorStrings[] = {
        "AuthcAMDenti",
        "GenuntelineI",
        "VMwawarereVM",
        "Micrt Hvosof"
    };
    const char* const infoStr = reinterpret_cast<const char*>(searchStart);
    std::uint8_t i = 0;
    for (; i < 4; ++i)
    {
        if (std::equal(infoStr, infoStr + 12, VendorStrings[i], VendorStrings[i] + 12))
            break;
    }
    return static_cast<Vendor>(i);
}

参考:

  1. https://en.wikipedia.org/wiki/CPUID
  2. https://software.intel.com/en-us/articles/intel-architecture-and-processor-identification-with-cpuid-model-and-family-numbers
  3. https://msdn.microsoft.com/en-us/library/hskdteyh.aspx
  4. https://software.intel.com/en-us/articles/how-to-detect-new-instruction-support-in-the-4th-generation-intel-core-processor-family
 类似资料: