首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> C++ >> C++ 入門

C++ CPUID 指令封裝實戰:獲取處理器特性、廠商信息與緩存拓撲

     編輯:C++ 入門

CPUID 指令基礎

  • x86 架構專用指令,通過 EAX 輸入功能碼 (葉/子葉),返回 EAX/EBX/ECX/EDX 四寄存器信息。
  • C/C++ 內聯匯編 (__asm MSVC / __asm__ GCC) 或編譯器內建函數 (__cpuid, __cpuidex) 執行。
  • 需在用戶態 (Ring 3) 可執行,無特權要求。

MSVC 內建函數封裝類 (推薦,無內聯匯編移植問題)


#include  // __cpuid, __cpuidex
#include 
#include 
#include 
#include 

class CpuId {
    std::array regs_; // eax, ebx, ecx, edx
public:
    // 執行 CPUID,leaf = 功能碼,subleaf = 子功能碼 (ECX 輸入)
    void execute(uint32_t leaf, uint32_t subleaf = 0) {
        __cpuidex(reinterpret_cast(regs_.data()), static_cast(leaf), static_cast(subleaf));
    }
    
    uint32_t eax() const { return regs_[0]; }
    uint32_t ebx() const { return regs_[1]; }
    uint32_t ecx() const { return regs_[2]; }
    uint32_t edx() const { return regs_[3]; }
    
    // 獲取廠商字符串 (12 字符)
    std::string vendorId() const {
        char str[13] = {0};
        // 順序:EBX, EDX, ECX (每寄存器 4 字節,低位在前)
        *reinterpret_cast(str + 0) = ebx();
        *reinterpret_cast(str + 4) = edx();
        *reinterpret_cast(str + 8) = ecx();
        return std::string(str);
    }
    
    // 獲取品牌字符串 (48 字符,Leaf 0x80000002-0x80000004)
    std::string brandString() const {
        std::string brand;
        brand.reserve(48);
        CpuId cpu;
        for (uint32_t leaf = 0x80000002; leaf <= 0x80000004; ++leaf) {
            cpu.execute(leaf);
            // 每葉返回 EAX, EBX, ECX, EDX 共 16 字符
            brand.append(reinterpret_cast(&cpu.eax()), 4);
            brand.append(reinterpret_cast(&cpu.ebx()), 4);
            brand.append(reinterpret_cast(&cpu.ecx()), 4);
            brand.append(reinterpret_cast(&cpu.edx()), 4);
        }
        return brand;
    }
    
    // 檢查特性位 (如 SSE4.2: Leaf 1, ECX bit 20)
    bool hasFeature(uint32_t leaf, uint32_t regIdx, uint32_t bit) const {
        return (regs_[regIdx] >> bit) & 1;
    }
};

關鍵葉功能碼速查

Leaf (EAX)用途關鍵輸出
0最大基本葉 / 廠商 IDEAX=max leaf; EBX/EDX/ECX="GenuineIntel"/"AuthenticAMD"
1處理器簽名/特性標誌EAX=簽名; ECX/EDX=特性位 (SSE, AVX, HT, VMX...)
2緩存/ TLB 描述符EAX/EBX/ECX/EDX=描述符字節 (需查表解析)
4確定性緩存參數EAX=緩存類型/級別/共享; EBX=大小參數; ECX=方式數
0x80000000最大擴展葉EAX=max ext leaf
0x80000001擴展特性標誌ECX/EDX=擴展特性 (LAHF/SAHF, LZCNT, PREFETCHW...)
0x80000002-4處理器品牌字符串EAX..EDX=48 字符品牌 (如 "Intel(R) Core(TM) i7-9700K")
0x80000005L1 緩存/TLBEAX/EBX/ECX/EDX=L1 數據/指令緩存信息
0x80000006L2/L3 緩存ECX=L2 大小/行大小/關聯度; EDX=L3 同

使用示例


int main() {
    CpuId cpu;
    
    // 1. 廠商
    cpu.execute(0);
    std::cout << "Vendor: " << cpu.vendorId() << std::endl; // "GenuineIntel"
    std::cout << "Max Basic Leaf: " << cpu.eax() << std::endl;
    
    // 2. 品牌
    std::cout << "Brand: " << cpu.brandString() << std::endl;
    
    // 3. 特性檢測
    cpu.execute(1);
    bool hasSSE42 = cpu.hasFeature(1, 2, 20); // ECX bit 20
    bool hasAVX = cpu.hasFeature(1, 2, 28);   // ECX bit 28
    bool hasAVX2 = false;
    // AVX2 在 Leaf 7 (EAX=7, ECX=0) EBX bit 5
    cpu.execute(7, 0);
    hasAVX2 = cpu.hasFeature(7, 1, 5); 
    
    std::cout << "SSE4.2: " << hasSSE42 << " AVX: " << hasAVX << " AVX2: " << hasAVX2 << std::endl;
    
    // 4. 緩存拓撲 (Leaf 4)
    for (uint32_t sub = 0; ; ++sub) {
        cpu.execute(4, sub);
        uint32_t type = (cpu.eax() >> 0) & 0x1F;
        if (type == 0) break; // 無更多緩存
        uint32_t level = (cpu.eax() >> 5) & 0x7;
        uint32_t ways = (cpu.ebx() >> 22) & 0x3FF + 1;
        uint32_t partitions = (cpu.ebx() >> 12) & 0x3FF + 1;
        uint32_t lineSize = (cpu.ebx() >> 0) & 0xFFF + 1;
        uint32_t sets = cpu.ecx() + 1;
        uint32_t sizeKB = (ways * partitions * lineSize * sets) / 1024;
        std::cout << "Cache L" << level << ": " << sizeKB << " KB, " << ways << "-way, " << lineSize << "B line" << std::endl;
    }
    return 0;
}

跨平台注意 (GCC/Clang)


// GCC/Clang 內建
#include 
void execute_gcc(uint32_t leaf, uint32_t subleaf, uint32_t out[4]) {
    __cpuid_count(leaf, subleaf, out[0], out[1], out[2], out[3]);
}
// 或內聯匯編 (x86_64)
__asm__("cpuid" : "=a"(eax), "=b"(ebx), "=c"(ecx), "=d"(edx) : "a"(leaf), "c"(subleaf));

總結

CPUID 是 x86 硬件信息查詢的唯一標準接口。使用編譯器內建函數 (__cpuid/__cpuidex) 封裝比內聯匯編更安全、可移植 (MSVC/GCC/Clang 均支持)。核心場景:運行時特性檢測 (SSE/AVX 分派)、緩存拓撲感知優化 (線程綁核、數據分塊)、硬件識別授權。注意:虛擬機可能隱藏/修改 CPUID 信息,關鍵邏輯需有回退方案。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved