zoukankan      html  css  js  c++  java
  • 如何在各个版本的VC及64位下使用CPUID指令

      前面我们探讨了在16位的DOS实模式下使用CPUID指令(http://www.cnblogs.com/zyl910/archive/2012/05/14/dos16_getcpuid.html)。而现在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.aspx
    void __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 <Windows.h>
    #include <stdio.h>
    #include <tchar.h>
    
    #if _MSC_VER >=1400    // VC2005才支持intrin.h
    #include <intrin.h>    // 所有Intrinsics函数
    #endif
    
    char szBuf[64];
    INT32 dwBuf[4];
    
    #if defined(_WIN64)
    // 64位下不支持内联汇编. 应使用__cpuid、__cpuidex等Intrinsics函数。
    #else
    #if _MSC_VER < 1600    // VS2010. 据说VC2008 SP1之后才支持__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;
        }
    }
    #endif    // #if _MSC_VER < 1600    // VS2010. 据说VC2008 SP1之后才支持__cpuidex
    
    #if _MSC_VER < 1400    // VC2005才支持__cpuid
    void __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. http://www.intel.com/content/www/us/en/architecture-and-technology/64-ia-32-architectures-software-developer-instruction-set-reference-manual-325383.html
    《Intel® Processor Identification and the CPUID Instruction》. April 2012. http://developer.intel.com/content/www/us/en/processors/processor-identification-cpuid-instruction-note.html
    《AMD CPUID Specification》. September 2010. http://support.amd.com/us/Embedded_TechDocs/25481.pdf
    《__cpuid, __cpuidex》. http://msdn.microsoft.com/en-us/library/hskdteyh.aspx
    《__cpuidex》. http://social.msdn.microsoft.com/Forums/en-US/vclanguage/thread/cac9c43b-ed72-4283-baa0-e7cd397591bc
    《Predefined Macros》. http://msdn.microsoft.com/en-us/library/b0084kay(v=vs.110).aspx
    《预定义_MSC_VER宏》. OwnWaterloo著。http://www.cppblog.com/ownwaterloo/archive/2009/04/15/predefined_macro__MSC_VER.html


    源码下载——
    https://files.cnblogs.com/zyl910/vcgetcpuid.rar

  • 相关阅读:
    mysql uodate 报错 You can't specify target table '**' for update in FROM clause
    设置mysql InnoDB存储引擎下取消自动提交事务
    SQL插入数据--数据中的某一列来自本表中的数据
    服务器部署静态页面
    json 和 jsonp
    Git 回滚
    java 自定义注解
    java BlockingQueque的多种实现
    java 多线程之ReentrantLock与condition
    storm 架构原理
  • 原文地址:https://www.cnblogs.com/zyl910/p/vcgetcpuid.html
Copyright © 2011-2022 走看看