摘要
简要介绍Intel架构处理器的CPUID指令的规范,在程序中的一般作用,以及微软Visual C++中对应的__cpuid / __cpuidex函数,和我对这两个函数做的简单的类封装。
下载 Intel® Processor Identification and the CPUID Instruction
CPUID 指令
具体描述参考上面的Intel官方文本,这里只做很简单的描述。加上一些Intel 241618文档里没有的内容。
CPUID汇编指令接受输入的寄存器是EAX,存放需要的子功能号,从0x00开始;存放输出的寄存器是EAX,EBX,ECX,EDX。CPUID的子功能集有两大类,基本和扩展。
基本功能:
EAX = 00H 获得最大可用的基本功能号,以及CPU Vendor ID
EAX = 01H 获得CPU各项基本属性,包括Family, Model, Stepping ID, 和其他特性支持描述符
EAX = 02H CPU缓存和TLB的基本描述
EAX = 03H CPU序列号 (由于隐私考虑,在Pentium III以后处理器中该功能被禁止)
扩展功能:
EAX = 80000000H 获得最大可用的扩展功能号
EAX = 80000001H 获得扩展CPU信息和支持的扩展CPU功能表
EAX = 80000002H,80000003H,80000004H 获得CPU描述字符串
EAX = 80000005H 获得CPU L1 Cache 和 TLB 属性描述
EAX = 80000006H 获得CPU L2 Cache 的属性描述
EAX = 80000007H 获得高级电源管理的属性描述 (APMI)
EAX = 80000008H 获得最大可能访问的机器物理地址和虚拟地址,在32位机器上,这个功能可以用来确定是否可以访问>4G内存。返回值存于EAX,0-7比特位存放物理地址比特位数,如果是32就是4G,如果是36就是64G;8-15比特位存放虚拟地址比特位数。
__cpuid / __cpuidex 函数
MSDN 链接 http://msdn.microsoft.com/en-us/library/hskdteyh(v=VS.100).aspx
这两个函数是微软对CPUID汇编指令的C++封装,易于使用,不再需要操作寄存器,而是int32数组,方便高级程序访问CPU信息。函数声明如下:
void __cpuid( int CPUInfo[4], int InfoType ); void __cpuidex( int CPUInfo[4], int InfoType, int ECXValue );
InfoType就是输入的EAX值,CPUInfo就是输出的int数组,CPUInfo[0],CPUInfo[1],CPUInfo[2],CPUInfo[3]分别对应EAX,EBX,ECX,EDX寄存器。ECXValue就是输入的ECX寄存器值。
面向对象封装
CPU.h
#include namespace yshi { class CPUID { public: enum Vendor {INTEL, AMD}; public: CPUID(); public: Vendor GetVendor() const; const char* GetVendorString() const; int GetProcessorSignature() const; int GetProcessorFamily() const; int GetProcessorModel() const; int GetProcessorSteppingID() const; private: Vendor DoGetVendor() const; int GetFamily() const; int GetExtendedFamily() const; int GetModel() const; int GetExtendedModel() const; private: Vendor _vendor; unsigned int _maxInfoType; unsigned int _info[16][4]; // Can grow in future unsigned int _maxInfoTypeEx; unsigned int _infoEx[16][4]; // Can grow in future }; }
CPU.cpp
#include "stdafx.h" #include "CPU.h" #include namespace yshi { CPUID::CPUID() { // Get maximum value for the InfoType parameter that will return valid information __cpuid((int*)_info[0], 0); _maxInfoType = _info[0][0]; _vendor = DoGetVendor(); for (unsigned int i = 1; i <= _maxInfoType; i++) { __cpuid((int*)_info[i], i); } // Get maximum value for the extended InfoType parameter that will return valid information __cpuid((int*)_infoEx[0], 0x80000000); _maxInfoTypeEx = _infoEx[0][0]; // Get extended features for (unsigned int i = 0x80000001; i <= _maxInfoTypeEx; i++) { __cpuid((int*)_infoEx[i], i); } } CPUID::Vendor CPUID::DoGetVendor() const { if (_info[0][1] == 0x756E6547 && _info[0][2] == 0x6C65746E && _info[0][3] == 0x49656E69) return INTEL; else if (_info[0][1] == 0x68747541 && _info[0][2] == 0x444D4163 && _info[0][3] == 0x69746E65) return AMD; else throw std::exception("Unsupported CPU vendor."); } CPUID::Vendor CPUID::GetVendor() const { return _vendor; } int CPUID::GetProcessorSignature() const { return _info[1][0] & 0x0FFF0FFF; } int CPUID::GetProcessorSteppingID() const { return _info[1][0] & 0x0000000F; } int CPUID::GetModel() const { return (_info[1][0] & 0x000000F0) >> 4; } int CPUID::GetExtendedModel() const { return (_info[1][0] & 0x000F0000) >> 16; } int CPUID::GetProcessorModel() const { return (GetExtendedModel() << 4) + GetModel(); } int CPUID::GetFamily() const { return (_info[1][0] & 0x00000F00) >> 8; } int CPUID::GetExtendedFamily() const { return (_info[1][0] & 0x0FF00000) >> 20; } int CPUID::GetProcessorFamily() const { return GetFamily() + GetExtendedFamily(); } }
主要用途
CPUID指令主要可用于判断Intel架构CPU的制造商,比如在一些细微的功能实现上,Intel和AMD有差别,要用到此功能的代码就需要先判断制造商.CPUID指令也可用来检测某功能是否支持,从而选择实现方式,比如判断当前CPU是否支持SSE2指令集,从而决定是采用优化浮点计算的SSE2,还是X87浮点计算.