编程实现在Linux系统中Intel CPU功耗的获取

编程实现在Linux系统中Intel CPU功耗的获取

  • 编程实现在Linux系统中Intel CPU功耗的获取
    • 资料的获取
    • X86 MSR寄存器介绍
    • X86 CPUID介绍
    • X86 RAPL介绍
    • Linux中挂载CPUID和MSR模块
    • 实现思路

编程实现在Linux系统中Intel CPU功耗的获取

资料的获取

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

X86 MSR寄存器介绍

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)

X86 CPUID介绍

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命令就能查看了。
编程实现在Linux系统中Intel CPU功耗的获取_第1张图片

X86 RAPL介绍

RAPL全称为: running average power limit
RAPL是x86处理器用于限制功耗、获取功耗的一组工具,rapl提供了一系列的设置接口用于限制和获取cpu的功耗以及CPU的能量消耗,而我们调用这些功能的入口就是学会读写msr寄存器。关于RAPL的操作说明,参考Intel开发者帮助手册第3卷14.9章节。

Linux中挂载CPUID和MSR模块

一般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

加载之后的效果:
编程实现在Linux系统中Intel CPU功耗的获取_第2张图片

实现思路

在RAPL中获取功耗的思路是:

  1. 首先使用cpuid代码或者lscpu查看cpu是否支持msr (这个一般都支持)
  2. 读取MSR_RAPL_POWER_UNIT寄存器的内容,得到能量的基本计算单元。
  3. 读取MSR_PKG_ENERGY_STATUS寄存器的内容,用来乘以能量基本计算单元,得到具体的累积能量消耗(单位:焦耳)。
  4. 程序暂停一段时间。
  5. 再次读取MSR_PKG_ENERGY_STATUS寄存器的内容,乘以能量基本计算单元,得到累积能量消耗。
  6. 用第二次得到的累积能量消耗减去第一次的,除以时间,就得到了这段时间的平均功耗。

尝试过直接使用power相关的寄存器去获取功耗,但是发现失败了。如果有成功的同志,欢迎一起讨论。

Intel开发者帮助手册14.9中,关于MSR_RAPL_POWER_UNIT的介绍:
编程实现在Linux系统中Intel CPU功耗的获取_第3张图片
Intel开发者帮助手册14.9中,关于MSR_PKG_ENERGY_STATUS的介绍:
编程实现在Linux系统中Intel CPU功耗的获取_第4张图片
关于不同的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

你可能感兴趣的:(程序员的玩乐,功耗获取,power,Intel,CPU,RAPL)