cpuid
是用来查询 CPU 相关信息的指令。其大致使用方式为:
eax
(有时会涉及 ecx
)放入指定的值cpuid
在 Visual C++ 以及 GCC 中,都有对应的 CPUID
的 intrin
:
#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
的数组,依次存放了执行 cpuid
后 eax
、ebx
、ecx
、edx
的值。
有了这个函数,剩下的只是查文档了。例如我想要拿到当前 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
设置为 0x80000002
、0x80000003
、0x80000004
,每次执行 cpuid
后,从 eax
、ebx
、ecx
、edx
取出长度为 16 的字符集合(没有空字符结尾)(每次 16 个,3 次共 48 个)。
再例如,我想得到 CPU 的生产厂商。将 eax
置 0 后调用 cpuid
,会在 ebx
、edx
、ecx
(注意这个 edx
和 ecx
和 info
的顺序是反的)放入长度为 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);
}
参考: