- 前言
- 一、cpuid指令简介
- 1.1 cpuid指令功能
- 1.2 cpuid指令代码
- 二、获取处理器信息
- 2.1 输入参数为0H
- 2.2 输入参数为01H
- 2.3 输入参数为0x80000002H
- 2.4 输入参数为0x80000008H
- 三、完整代码演示
- 总结
- 参考资料
前言
写了一个简易版的Linux 系统下获取cpu的信息,后面会完善,目前先获取一些最基本的处理器信息,代码主要参考Linux内核源码,基本是移植过来的。。。。。。
在这篇文章我介绍过cpuid指令:
Intel x86_64 CPUID指令介绍
这里就不介绍那么详细啦。
主要用来获取处理器身份和特征信息。
我在应用层使用内嵌汇编 asm volatile,调用cpuid指令,根据输入不同的值,将处理器的信息作为返回值写在eax、ebx、ecx、edx中。
static void cpuid(unsigned int op, unsigned int *eax, unsigned int *ebx, unsigned int *ecx, unsigned int *edx)
{
*eax = op;
*ecx = 0;
asm volatile("cpuid" //内嵌汇编指令 cpuid
: "=a" (*eax), //输出参数
"=b" (*ebx),
"=c" (*ecx),
"=d" (*edx)
: "0" (*eax), "2" (*ecx) //输入参数
: "memory");
}
二、获取处理器信息
2.1 输入参数为0H
当输入参数为0H时,用来获取厂商标识字符串信息,如Intel,AMD等等。
Intel:“GenuineIntel”
AMD:“AuthenticAMD”
代码如下:
int cpuid_level;
char x86_vendor_id[16] = {0};
cpuid(0x00000000, (unsigned int *)&cpuid_level,
(unsigned int *)&x86_vendor_id[0],
(unsigned int *)&x86_vendor_id[8],
(unsigned int *)&x86_vendor_id[4]);
这样供应商的信息就存放在x86_vendor_id数组中了。
2.2 输入参数为01H当输入参数为01H时,在返回值EAX寄存器中就可以获得处理器的 DisplayFamily_DisplayModel,DisplayFamily_DisplayModel 信息通常用以识别特定的处理器。
代码如下(示例):
unsigned int x86_family(unsigned int sig)
{
unsigned int x86;
x86 = (sig >> 8) & 0xf;
if (x86 == 0xf)
x86 += (sig >> 20) & 0xff;
return x86;
}
unsigned int x86_model(unsigned int sig)
{
unsigned int fam, model;
fam = x86_family(sig);
model = (sig >> 4) & 0xf;
if (fam >= 0x6)
model += ((sig >> 16) & 0xf) << 4;
return model;
}
unsigned int x86_stepping(unsigned int sig)
{
return sig & 0xf;
}
2.3 输入参数为0x80000002H
当输入参数为0x80000002H,0x80000003H,0x80000004H时用来获取处理器品牌字符串,如:
static void get_model_name()
{
char x86_model_id[64] = {0};
unsigned int *v = (unsigned int *)x86_model_id;
cpuid(0x80000002, &v[0], &v[1], &v[2], &v[3]);
cpuid(0x80000003, &v[4], &v[5], &v[6], &v[7]);
cpuid(0x80000004, &v[8], &v[9], &v[10], &v[11]);
x86_model_id[48] = 0;
}
2.4 输入参数为0x80000008H
当输入参数为0x80000008H时,获取物理地址大小信息和虚拟地址信息大小。如:
void get_cpu_address_sizes()
{
unsigned int eax, ebx, ecx, edx;
cpuid(0x80000008, &eax, &ebx, &ecx, &edx);
x86_virt_bits = (eax >> 8) & 0xff;
x86_phys_bits = eax & 0xff;
}
三、完整代码演示
#include#include struct cpuinfo_x86 { unsigned char x86; unsigned char x86_vendor; unsigned char x86_model; unsigned char x86_stepping; int cpuid_level; char x86_vendor_id[16]; char x86_model_id[64]; int x86_cache_alignment; unsigned short x86_clflush_size; unsigned char x86_virt_bits; unsigned char x86_phys_bits; unsigned char x86_cache_bits; }; static inline void cpuid(unsigned int op, unsigned int *eax, unsigned int *ebx, unsigned int *ecx, unsigned int *edx) { *eax = op; *ecx = 0; asm volatile("cpuid" : "=a" (*eax), "=b" (*ebx), "=c" (*ecx), "=d" (*edx) : "0" (*eax), "2" (*ecx) : "memory"); } unsigned int x86_family(unsigned int sig) { unsigned int x86; x86 = (sig >> 8) & 0xf; if (x86 == 0xf) x86 += (sig >> 20) & 0xff; return x86; } unsigned int x86_model(unsigned int sig) { unsigned int fam, model; fam = x86_family(sig); model = (sig >> 4) & 0xf; if (fam >= 0x6) model += ((sig >> 16) & 0xf) << 4; return model; } unsigned int x86_stepping(unsigned int sig) { return sig & 0xf; } static void get_model_name(struct cpuinfo_x86 *c) { unsigned int *v = (unsigned int *)c->x86_model_id; cpuid(0x80000002, &v[0], &v[1], &v[2], &v[3]); cpuid(0x80000003, &v[4], &v[5], &v[6], &v[7]); cpuid(0x80000004, &v[8], &v[9], &v[10], &v[11]); c->x86_model_id[48] = 0; } void cpu_detect(struct cpuinfo_x86 *c) { memset(c->x86_vendor_id, 0, sizeof(c->x86_vendor_id)); cpuid(0x00000000, (unsigned int *)&c->cpuid_level, (unsigned int *)&c->x86_vendor_id[0], (unsigned int *)&c->x86_vendor_id[8], (unsigned int *)&c->x86_vendor_id[4]); c->x86 = 4; if (c->cpuid_level >= 0x00000001) { unsigned int junk, tfms, cap0, misc; cpuid(0x00000001, &tfms, &misc, &junk, &cap0); c->x86 = x86_family(tfms); c->x86_model = x86_model(tfms); c->x86_stepping = x86_stepping(tfms); if (cap0 & (1<<19)) { c->x86_clflush_size = ((misc >> 8) & 0xff) * 8; c->x86_cache_alignment = c->x86_clflush_size; } } } void get_cpu_address_sizes(struct cpuinfo_x86 *c) { unsigned int eax, ebx, ecx, edx; cpuid(0x80000008, &eax, &ebx, &ecx, &edx); c->x86_virt_bits = (eax >> 8) & 0xff; c->x86_phys_bits = eax & 0xff; c->x86_cache_bits = c->x86_phys_bits; } int main() { unsigned int eax = 0; unsigned int ebx = 0; unsigned int ecx = 0; unsigned int edx = 0; struct cpuinfo_x86 _cpuinfo_x86; cpuid(0, &eax, &ebx, &ecx, &edx); printf("EBX ← %x (“Genu”)EDX ← %x (“ineI”) ECX ← %x (“ntel”)n", ebx, edx ,ecx); get_cpu_address_sizes(&_cpuinfo_x86); cpu_detect(&_cpuinfo_x86); get_model_name(&_cpuinfo_x86); printf("Address sizes: phys_bits = %d virt_bits = %dn", _cpuinfo_x86.x86_phys_bits, _cpuinfo_x86.x86_virt_bits); printf("Vendor Id= %sn", _cpuinfo_x86.x86_vendor_id); printf("cpuid level = %dn", _cpuinfo_x86.cpuid_level); printf("CPU family = %dn", _cpuinfo_x86.x86); printf("Model = %dn", _cpuinfo_x86.x86_model); printf("Stepping = %dn", _cpuinfo_x86.x86_stepping); printf("Model name = %sn", _cpuinfo_x86.x86_model_id); printf("clflush_size = %dn", _cpuinfo_x86.x86_clflush_size); printf("cache_alignment = %dn", _cpuinfo_x86.x86_cache_alignment); return 0; }
看看结果:
与lscpu显示的消息一致(也可以用cat /proc/cpuinfo查看):
主要是参考了 Linux内核源码 5.13.0中获取CPU信息的代码:
arch/x86/kernel/cpu/common.c
arch/x86/include/asm/processor.h
实验环境:
vmware + ubuntu 20.04
Linux内核源码 5.13.0
Intel官方手册 vol2



