Intel的CPU相关功能,Intel公司做了一个非常全面的帮助手册,开发人员想查找的所有功能,基本上在这个开发手册里面都有了。
传送门: https://software.intel.com/zh-cn/articles/intel-sdm (好像对国内网络不太好)
Intel 软件开发者帮助手册: 链接: https://pan.baidu.com/s/18c9hU8h-Cel7VqPmGqiGuQ 提取码: ztrg (下载了官网全卷的帮助文档pdf版,如果第一个链接能进,就用第一个链接吧!)
msr-tool 工具地址: https://01.org/zh/msr-tools?langredirect=1
MSR(Model Specific Register)是x86架构中的概念,指的是在x86架构处理器中,一系列用于控制CPU运行、功能开关、调试、跟踪程序执行、监测CPU性能等方面的寄存器。
MSR寄存器的雏形开始于Intel 80386和80486处理器,到Intel Pentium处理器的时候,Intel就正式引入RDMSR和WRMSR两个指令用于读和写MSR寄存器,这个时候MSR就算被正式引入。在引入RDMSR和WRMSR指令的同时,也引入了CPUID指令,该指令用于指明具体的CPU芯片中,哪些功能是可用的,或者这些功能对应的MSR寄存器是否存在,软件可以通过CPUID指令查询某些功能是否在当前CPU上是否支持。
每个MSR寄存器都会有一个相应的ID,即MSR Index,或者也叫作MSR寄存器地址,当执行RDMSR或者WRMSR指令的时候,只要提供MSR Index就能让CPU知道目标MSR寄存器。这些MSR寄存器的编号(MSR Index)、名字及其各个数据区域的定义可以在Intel x86架构手册”Intel 64 and IA-32 Architectures Software Developer’s Manual"的Volume 4中找到。
(以上内容摘自知乎作者河马的文章x86 CPU的MSR寄存器: https://zhuanlan.zhihu.com/p/50142793)
CPUID操作码是一个面向x86架构的处理器补充指令,它的名称派生自CPU识别,作用是允许软件发现处理器的详细信息。它由英特尔在1993年引入奔腾和SL增强486处理器。
通过使用CPUID操作码,软件可以确定处理器的类型和特性支持(例如MMX/SSE)。
关于CPUID的详细操作,见维基百科英文版说明: https://en.wikipedia.org/wiki/CPUID
这里通过CPUID指令获取cpu的vendor ID字符串为例:
在调用cpuid汇编指令之前,通过给eax寄存器赋值,然后执行cpuid指令,cpu就会将想获取的数据放入eax,ebx,ecx,edx四个寄存器中。
通过阅读危机百科英文版中CPUID关于EAX=0的部分,得到如下信息:
首先在调用cpuid汇编命令之前,将eax寄存器的值赋值为0,然后调用cpuid完成之后,cpu会将返回的12个字节的字符串分别放入ebx,edx,ecx三个寄存器中,然后编写如下程序代码:
#include
#include
typedef unsigned int uint32_t;
struct cpuid_info{
uint32_t eax;
uint32_t ebx;
uint32_t ecx;
uint32_t edx;
};
void get_cpuinfo(uint32_t eax, uint32_t ecx, struct cpuid_info* cpuid) {
asm(
"cpuid":
"=a"(cpuid->eax), "=b"(cpuid->ebx), "=c"(cpuid->ecx), "=d"(cpuid->edx):
"a"(eax), "c"(ecx)
);
}
char cpu_ID[13];
void get_vendor_ID(char* vendorID) {
struct cpuid_info cpuinfo;
get_cpuinfo(0, 0, &cpuinfo);
strncpy(vendorID, (const char*)&cpuinfo.ebx, 4);
strncpy(vendorID+4, (const char*)&cpuinfo.edx, 4);
strncpy(vendorID+8, (const char*)&cpuinfo.ecx, 4);
vendorID[13] = '\0';
}
int main(int argc, char** argv) {
get_vendor_ID(cpu_ID);
printf("vendor ID: %s\n", cpu_ID);
return 0;
}
其中get_cpuid函数即为通过cpuid获取运行当前程序的cpu的vendor ID信息,get_vendor_ID函数即为获取将寄存器里面的值组合为一个完整的字符串。
通过阅读CPUID相关的信息,我们得知当设置eax=1时,cpu是否支持msr寄存器的标志位就在edx寄存器的5比特位上。
当然,这些信息在Linux里面已经有工具帮我们实现好了,使用lscpu命令就能查看了。
RAPL全称为: running average power limit
RAPL是x86处理器用于限制功耗、获取功耗的一组工具,rapl提供了一系列的设置接口用于限制和获取cpu的功耗以及CPU的能量消耗,而我们调用这些功能的入口就是学会读写msr寄存器。关于RAPL的操作说明,参考Intel开发者帮助手册第3卷14.9章节。
一般Linux中,给了我们选择挂载CPUID和MSR模块的功能,一旦模块挂载之后,便不用使用汇编去在cpu中获取了,直接通过操作文件的形式操作/dev/cpu/[cpu]/cpuid和/dev/cpu/[cpu]/msr两个文件来读取和修改相关数值了。相关的操作可以查看资料里面的msr-tool源码:
/dev/cpu/[cpu]/cpuid中的信息,通过eax+(ecx<<32)作为文件的偏移来读取。
/dev/cpu/[cpu]/msr中的信息,通过msr寄存器的索引作为文件的偏移来读取和写入。
加载CPUID和MSR模块的命令:
sudo modprobe cpuid
sudo modprobe msr
在RAPL中获取功耗的思路是:
尝试过直接使用power相关的寄存器去获取功耗,但是发现失败了。如果有成功的同志,欢迎一起讨论。
Intel开发者帮助手册14.9中,关于MSR_RAPL_POWER_UNIT的介绍:
Intel开发者帮助手册14.9中,关于MSR_PKG_ENERGY_STATUS的介绍:
关于不同的cpu中MSR_RAPL_POWER_UNIT和MSR_PKG_ENERGY_STATUS寄存器的具体索引数值,可以在Intel开发者帮助手册第四卷里面找到。一般MSR_RAPL_POWER_UNIT的索引地址为:0x606,一般MSR_PKG_ENERGY_STATUS的索引地址为:0x611 。
实现代码这里就不贴上来了,具体代码实现见项目地址。
码云地址: https://gitee.com/xiaobai_Lee/intel_power_consumption_get
github地址: https://github.com/lixiaobai09/intel_power_consumption_get