首页 > 编程知识 正文

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

时间:2023-05-05 04:38:35 阅读:239545 作者:3740

编程实现在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 <stdio.h>#include <string.h>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] = '';}int main(int argc, char** argv) { get_vendor_ID(cpu_ID); printf("vendor ID: %sn", cpu_ID); return 0;}

其中get_cpuid函数即为通过cpuid获取运行当前程序的cpu的vendor ID信息,get_vendor_ID函数即为获取将寄存器里面的值组合为一个完整的字符串。
通过阅读CPUID相关的信息,我们得知当设置eax=1时,cpu是否支持msr寄存器的标志位就在edx寄存器的5比特位上。
当然,这些信息在Linux里面已经有工具帮我们实现好了,使用lscpu命令就能查看了。

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 cpuidsudo modprobe msr

加载之后的效果:

实现思路

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

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

尝试过直接使用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

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。