前段时间利用Superio获取到了CPU风扇速度,并且利用Superio读取到了CPU的温度(Intel PCIE),但通过sensors命令对比,发现封装的CPU温度和核心温度有误差,于是想通过代码获取下温度,来测试下是否正确。在网上查找了很多资料,这里记录一下成果。
获取Intel CPU 信息和温度
CPU信息可以通过cpuid指令获取,在用户空间可以通过内嵌汇编代码实现,代码如下:
1 | struct cpuid_res { |
获取厂家名称
以eax=0 执行 cpuid,eax为0表示读取vendor id,一共12字节,依次在ebx、edx、ecx。
1 | result = cpuid(0); |
获取芯片型号
用cpuid指令,eax传入分别0x80000002/0x80000003/0x80000004,读取cpu型号,每个4个寄存器,每个寄存器4字节,一共48字节。
1 | struct cpuid_res res; |
获取CPU温度
Intel和AMD的CPU中都有温度传感器(DTS),每个核心都有一个,温度就是由此获取来的。Intel对CPU温度的处理,设置了一个最高温度Tjunction,从MSR中读取的数据为与最高温度的温差Delta,并非实际温度,实际温度为Tjunction-Delta。
检查CPU是否支持DTS
先以eax=0 执行 cpuid 检测 eax 支持的最大命令数,如果小于6就肯定不支持DTS。
1 | int level = cpuid(0).eax; |
以eax=6 执行 cpuid, 然后测试 eax 第一位是否为1,如果为1表示CPU支持DTS。
1 | int val = cpuid(6).eax; |
读取DTS
要获取cpu的温度可以通过汇编指令来读取。但linux环境下,msr指令必须要在内核层才能调用,这里我在驱动中中实现ioctl接口,然后返回数据给用户层。
1 | uint32_t lo, hi; |
Tjunction:当核心温度达到了阀值,会通过降频、降压、风扇调节等形式调节温度。
以 ecx=0x1A2 执行 rdmsr 指令,通过0x1A2来读取MSR的[16-22]位得到Tjunction。
1 | u64 __val = __rdmsr(0x1A2);//<asm/msr.h> |
Delta:我们从MSR读到的温度是距离Tjunction的温差,而不是实际温度
以 ecx=0x19C 执行 rdmsr 指令,通过0x19C来读取MSR的[16-22]位得到Delta。
1 | u64 __val2 = __rdmsr(0x19C); |
当前cpu温度 = Tjunction - Delta
核心代码:
1 | u64 __val = __rdmsr(0x1A2); |
通过对比sensors,这里获取的是Core 1的温度,Core 0的温度可以通过切换核心读取到。
代码地址:https://github.com/huchanghui123/my_cdev/blob/master/my_dev.c