博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
如何在各个版本的VC及64位下使用CPUID指令
阅读量:6080 次
发布时间:2019-06-20

本文共 4927 字,大约阅读时间需要 16 分钟。

  前面我们探讨了在16位的DOS实模式下使用CPUID指令()。而现在64位Windows系统已经很流行了,在32/64位模式下如何使用CPUID呢?于是本文介绍了如何在各个版本的VC及64位下使用CPUID指令。

一、推荐使用__cpuid、__cpuidex等Intrinsics函数

  在32位模式下,我们可以使用内嵌汇编来调用cpuid指令。但在64位模式下,VC编译器不支持内嵌汇编。

  于是微软提供了Intrinsics函数——编译器会将Intrinsics函数编译为对应的机器指令,而且同时支持32位和64位。
  例如CPUID指令的对应Intrinsics函数是——

// http://msdn.microsoft.com/en-us/library/hskdteyh.aspxvoid __cpuid(   int CPUInfo[4],   int InfoType);void __cpuidex(   int CPUInfo[4],   int InfoType,   int ECXValue);

  __cpuidex函数的InfoType参数是CPUID指令的eax参数,即功能ID。ECXValue参数是CPUID指令的ecx参数,即子功能ID。CPUInfo参数用于接收输出的eax, ebx, ecx, edx这四个寄存器。
  早期的CPUID功能只需要一个功能ID参数(eax),这时可以使用__cpuid函数。
  后来CPUID的功能越来越强大,一个功能ID参数(eax)参数不够用,于是加了一个子功能ID(ecx)参数,这时应该采用__cpuidex。

二、用条件编译判断VC编译器对Intrinsics函数的支持性(_MSC_VER)

  在__cpuid、__cpuidex等Intrinsics函数时,会遇到以下问题——

1.低版本的VC编译器没有intrin.h头文件。【注】:只有VC2005(或更高)才拥有intrin.h,支持__cpuid。
2.低版本的VC编译器不支持__cpuidex。【注】:只有VC2008的部分版本及VS2010(或更高)的intrin.h中才有__cpuidex。

  这时可以使用条件编译来判断VC编译器的版本。

  _MSC_VER是微软C/C++编译器——cl.exe编译代码时预定义的一个宏,它的值表示cl的版本,它的类型是“int”。例如——
#if _MSC_VER >=1200 // VC++6.0以上
#if _MSC_VER >=1300 // VC2003以上
#if _MSC_VER >=1400 // VC2005以上
#if _MSC_VER >=1500 // VC2008以上
#if _MSC_VER >=1600 // VC2010以上

  例如发现_MSC_VER大于等于1400时,我们可以#include <intrin.h>。然后再利用_MSC_VER进一步判断__cpuid、__cpuidex的支持性。

三、用条件编译判断64位模式(_WIN64)

  使用_WIN64这个预处理宏可用来判断目标平台是不是64位。

  虽然在编译x64平台的程序时,编译器会自动推导出_WIN64。但是Visual Studio的语法高亮不清楚这些,它有可能仍是按32位代码来做语法高亮。所以,建议还是手动在项目的预处理宏中增加_WIN64。

四、32位下用内嵌汇编实现__cpuidex函数

  在32位模式下,我们可以使用内嵌汇编来实现__cpuidex函数。代码如下——

void __cpuidex(INT32 CPUInfo[4], INT32 InfoType, INT32 ECXValue){    if (NULL==CPUInfo)    return;    _asm{        // load. 读取参数到寄存器        mov edi, CPUInfo;    // 准备用edi寻址CPUInfo        mov eax, InfoType;        mov ecx, ECXValue;        // CPUID        cpuid;        // save. 将寄存器保存到CPUInfo        mov    [edi], eax;        mov    [edi+4], ebx;        mov    [edi+8], ecx;        mov    [edi+12], edx;    }}

 

 

五、全部代码

  全部代码——

#include 
#include
#include
#if _MSC_VER >=1400 // VC2005才支持intrin.h#include
// 所有Intrinsics函数#endifchar szBuf[64];INT32 dwBuf[4];#if defined(_WIN64)// 64位下不支持内联汇编. 应使用__cpuid、__cpuidex等Intrinsics函数。#else#if _MSC_VER < 1600 // VS2010. 据说VC2008 SP1之后才支持__cpuidexvoid __cpuidex(INT32 CPUInfo[4], INT32 InfoType, INT32 ECXValue){ if (NULL==CPUInfo) return; _asm{ // load. 读取参数到寄存器 mov edi, CPUInfo; // 准备用edi寻址CPUInfo mov eax, InfoType; mov ecx, ECXValue; // CPUID cpuid; // save. 将寄存器保存到CPUInfo mov [edi], eax; mov [edi+4], ebx; mov [edi+8], ecx; mov [edi+12], edx; }}#endif // #if _MSC_VER < 1600 // VS2010. 据说VC2008 SP1之后才支持__cpuidex#if _MSC_VER < 1400 // VC2005才支持__cpuidvoid __cpuid(INT32 CPUInfo[4], INT32 InfoType){ __cpuidex(CPUInfo, InfoType, 0);}#endif // #if _MSC_VER < 1400 // VC2005才支持__cpuid#endif // #if defined(_WIN64)// 取得CPU厂商(Vendor)//// result: 成功时返回字符串的长度(一般为12)。失败时返回0。// pvendor: 接收厂商信息的字符串缓冲区。至少为13字节。int cpu_getvendor(char* pvendor){ INT32 dwBuf[4]; if (NULL==pvendor) return 0; // Function 0: Vendor-ID and Largest Standard Function __cpuid(dwBuf, 0); // save. 保存到pvendor *(INT32*)&pvendor[0] = dwBuf[1]; // ebx: 前四个字符 *(INT32*)&pvendor[4] = dwBuf[3]; // edx: 中间四个字符 *(INT32*)&pvendor[8] = dwBuf[2]; // ecx: 最后四个字符 pvendor[12] = '\0'; return 12;}// 取得CPU商标(Brand)//// result: 成功时返回字符串的长度(一般为48)。失败时返回0。// pbrand: 接收商标信息的字符串缓冲区。至少为49字节。int cpu_getbrand(char* pbrand){ INT32 dwBuf[4]; if (NULL==pbrand) return 0; // Function 0x80000000: Largest Extended Function Number __cpuid(dwBuf, 0x80000000); if (dwBuf[0] < 0x80000004) return 0; // Function 80000002h,80000003h,80000004h: Processor Brand String __cpuid((INT32*)&pbrand[0], 0x80000002); // 前16个字符 __cpuid((INT32*)&pbrand[16], 0x80000003); // 中间16个字符 __cpuid((INT32*)&pbrand[32], 0x80000004); // 最后16个字符 pbrand[48] = '\0'; return 48;}int _tmain(int argc, _TCHAR* argv[]){ //__cpuidex(dwBuf, 0,0); //__cpuid(dwBuf, 0); //printf("%.8X\t%.8X\t%.8X\t%.8X\n", dwBuf[0],dwBuf[1],dwBuf[2],dwBuf[3]); cpu_getvendor(szBuf); printf("CPU Vendor:\t%s\n", szBuf); cpu_getbrand(szBuf); printf("CPU Name:\t%s\n", szBuf); return 0;}

 

六、兼容性说明

  VC编译器对32/64位的支持性——

32位:VC6是最早支持编译32位Intrinsics函数的。
64位:VC2005是最早支持编译64位Intrinsics函数的。

  本文方法在32位编译器下的兼容性——

__cpuid:兼容VC6(或更高)。
__cpuidex:兼容VC6(或更高)。

  本文方法在64位编译器下的兼容性——

__cpuid:兼容VC2005(或更高)。
__cpuidex:兼容VC2010(或更高)。

 

参考文献——
《Intel® 64 and IA-32 Architectures Software Developer’s Manual Volume 2 (2A, 2B & 2C): Instruction Set Reference, A-Z》. May 2012.
《Intel® Processor Identification and the CPUID Instruction》. April 2012.
《AMD CPUID Specification》. September 2010.
《__cpuid, __cpuidex》.
《__cpuidex》.
《Predefined Macros》.
《预定义_MSC_VER宏》. OwnWaterloo著。

 

源码下载——

 

转载地址:http://rshgx.baihongyu.com/

你可能感兴趣的文章
nodejs流之行读取器例子
查看>>
源码|HDFS之NameNode:启动过程
查看>>
[译] 什么是Javascript中的提升
查看>>
阿里巴巴、百度、腾讯都在用的Java架构师知识体系
查看>>
Python 异步网络爬虫 I
查看>>
像 QQ 一样处理滑动冲突
查看>>
01、Handler的那些事
查看>>
Mac OS X x64 环境下覆盖objective-c类结构并通过objc_msgSend获得RIP执行shellcode
查看>>
[译] 如何写出更好的 React 代码?
查看>>
Android动画:这里有一份很详细的 属性动画 使用攻略
查看>>
RxJava2 实战知识梳理(5) 简单及进阶的轮询操作
查看>>
js call,apply,bind总结
查看>>
Spring Boot 中使用 Java API 调用 lucene
查看>>
从 Java 层看 React-Native 通信机制
查看>>
来来来!关于iOS基础总结咱俩好好唠唠
查看>>
兑吧:从自建HBase迁移到阿里云HBase实战经验
查看>>
ECS 控制台诊断系统
查看>>
聊聊servicecomb-saga的alpha-server
查看>>
iOS多线程调研
查看>>
iOS多线程Pthreads篇
查看>>