写了一个简易版的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");
}
当输入参数为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数组中了。
当输入参数为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;
}
当输入参数为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;
}
当输入参数为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)
{
/* Get vendor name */
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;
/* Intel-defined flags: level 0x00000001 */
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 = %d\n", _cpuinfo_x86.x86_phys_bits, _cpuinfo_x86.x86_virt_bits);
printf("Vendor Id= %s\n", _cpuinfo_x86.x86_vendor_id);
printf("cpuid level = %d\n", _cpuinfo_x86.cpuid_level);
printf("CPU family = %d\n", _cpuinfo_x86.x86);
printf("Model = %d\n", _cpuinfo_x86.x86_model);
printf("Stepping = %d\n", _cpuinfo_x86.x86_stepping);
printf("Model name = %s\n", _cpuinfo_x86.x86_model_id);
printf("clflush_size = %d\n", _cpuinfo_x86.x86_clflush_size);
printf("cache_alignment = %d\n", _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