CPU核心温度获取

转自:http://blog.csdn.net/xiaibiancheng/article/details/8979011

要获取cpu的温度可以通过汇编指令来读取,这里以intel cpu为例,用rdmsr指令读取 IA32_THERM_STATUS_MSR(0x019C)的值,然后用TjunctionMax 减去这个值就是当前cpu的温度,对于一般的intel cpu 的TjunctionMax值是固定的,比如我的cpu是Intel Core 2 (45nm),在官网上可以查到其值是100摄氏度,到后面的intel cpu专门有个寄存器IA32_TEMPERATURE_TARGET(0x01A2)保存TjunctionMax的值,可以通过rdmsr指令读取。但是rdmsr指令只能在Ring0层运行,在运用层是执行不了的,必须通过驱动的方式才能执行,在驱动层专门有个函数__readmsr负责读取类似IA32_THERM_STATUS_MSR(0x019C)

IA32_TEMPERATURE_TARGET特殊寄存器的值,所以只要在驱动层写好调用程序后在上层用DeviceIoControl函数就可读取这些特殊寄存器的值,信号别人已经写好了现有的驱动(驱动名 WinRing0.sys),你只要在你的程序里面加载这个驱动然后就可读取这些寄存器的值,获得intel cpu的温度。

其中在上层读取这些寄存器的主要代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
BOOL WINAPI Rdmsr(DWORD index, PDWORD eax, PDWORD edx)
{
    if(gHandle == INVALID_HANDLE_VALUE)
    {
        return FALSE;
    }
    if(eax == NULL || edx == NULL || gIsMsr == FALSE)
    {
        return FALSE;
    }
    DWORD   returnedLength = 0;
    BOOL    result = FALSE;
    BYTE    outBuf[8] = {0};
    result = DeviceIoControl(
        gHandle,
        IOCTL_OLS_READ_MSR,
        &index,
        sizeof(index),
        &outBuf,
        sizeof(outBuf),
        &returnedLength,
        NULL
        );
    if(result)
    {
        memcpy(eax, outBuf, 4);
        memcpy(edx, outBuf + 4, 4);
    }
    if(result)
    {
        return TRUE;
    }
    else
    {
        return FALSE;
    }
}

如果是多核cpu可以通过 SetThreadAffinityMask函数切换cpu来获取每个核的温度代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
void   CCPUTemperatureDlg :: OnTimer ( UINT_PTR   nIDEvent )
{
    
CEdit   * edit =( CEdit *) GetDlgItem ( IDC_EDIT1 );
    
CEdit   * edit1 =( CEdit *) GetDlgItem ( IDC_EDIT2 );
    
DWORD   eax = 0 , edx = 0 ;
    
ULONG   result ;
    
char   s [ 20 ];
    
result = SetThreadAffinityMask ( GetCurrentThread (), 1 );
    
Rdmsr ( 0x19c ,& eax ,& edx ); //read Temperature
     SetThreadAffinityMask ( GetCurrentThread (), result );
    
sprintf ( s , "%d" , 100 -(( eax & 0x007f0000 )>> 16 ));
    
edit -> SetWindowText ( s );
    
result = SetThreadAffinityMask ( GetCurrentThread (), 2 );
    
Rdmsr ( 0x19c ,& eax ,& edx ); //read Temperature
     SetThreadAffinityMask ( GetCurrentThread (), result );
    
sprintf ( s , "%d" , 100 -(( eax & 0x007f0000 )>> 16 ));
    
edit1 -> SetWindowText ( s );
    
CDialog :: OnTimer ( nIDEvent );
}

下面是读取结果图:


程序的下载地址 http://download.csdn.net/detail/xiaibiancheng/5491513

win7系统运行时要以管理员的方式运行,不然驱动不能加载,还有杀毒软件也会阻止驱动的加载


-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

转自:http://www.68idc.cn/help/buildlang/ask/20150310264569.html

最近在研究怎样获取CPU的温度,网上也有一些办法,但都不算完整,没有清晰的解决方案,现在把我的方法完整的说一下,其实是很简单的东西,没有什么很复杂的。有些地方班门弄斧,各位大侠多多担待。因为我用的是Intel的CPU,所以只做了Intel的,APU的没办法测试,感兴趣的可以研究。

Intel从Core Duo处理器开始,每一个物理核心都有一个温度传感器(DTS-Digital Thermal Sensor)用来获取核心温度,这是Intel推荐的获取温度的方法,因为DTS处在每个物理核心温度最高的位置。这个传感器的温度值是通过MSR寄存器来获得的。MSR是什么大家都知道吧,具体可以参考Developer's manual第3卷第35章。在我的下载里面有Intel的manual。

通过DTS获取温度并不是直接得到CPU的实际温度,而是两个温度的差。第一个叫做Tjmax,这个Intel叫TCC activation temperature,意思是当CPU温度达到或超过这个值时,就会触发相关的温度控制电路,系统此时会采取必要的动作来降低CPU的温度,或者直接重启或关机。所以CPU的温度永远不会超过这个值。这个值一般是100℃或85℃(也有其他值),对于具体的处理器来说就是一个固定的值。第二个就是DTS获取的CPU温度相对Tjmax的偏移值,暂且叫Toffset,那CPU的实际温度就是:currentTemp=Tjmax - Toffset。

这两个温度值都是通过MSR来获得,获得MSR寄存器中的值用汇编指令rdmsr,Tjmax值相关的MSR的Signature是1A2H,执行

mov ecx, 0x1A2

rdmsr

后,eax中16~23位就是Tjmax的值。

同理,Toffset值相关的MSR的Signature是19CH,执行

mov ecx, 0x19C

rdmsr

后,eax中16~22(注意这里是7位)位就是Toffset的值。

问题在于,rdmsr指令需要ring0权限,而Windows下应用程序的权限都是ring3,所以如果在C中直接build-in汇编执行,程序立即停止工作。

于是我在网上猛搜怎样获取CPU的ring0权限,可惜没有找到相关的代码。这时我参考了Open Hardware Monitor这个软件,Open Hardware Monitor是用C sharp写的,可以检测各个硬件的温度和频率等,可惜我看不懂C#代码。但在里面找到了WinRing0.sys,WinRing0也是开源的,看到它的实现之后顿时大吃一惊,里面直接提供rdmsr指令的C函数,已经帮你绕过了Windows的重重城墙,当时下巴就掉下来了。

所以直接调用Rdmsr()函数就可以了,没有其它。当然要具体了解Winring0是怎样获得ring0权限的,可以直接看它的代码。

在执行MSR读取时,要先用CPUID判断处理器是否支持DTS,最近的处理器都是支持的。具体是CPUID.06H:EAX[bit0]是否被置位。置1时就是可以的。

另外,我的是处理器是4核,每个物理核心都应该对应一个温度,可我只获得了一个。跟Open Hardware Monitor对比之后,这个值总是4个核心中最小的那个,怎样获得4个核心加一个package的温度,还需要再研究。无聊还跟鲁大娘对比了一下,大娘不太靠谱,在我的处理器上低了大概10度。CPU负载突然变大时,温度会瞬间提高,大娘基本没反应。

网上还有另处一种方法我觉得是可行的,是读PMU值,端口号是68H和6CH,同样是绕过Windows来获得ring0权限,用的是WinI/O,不过我没有试。

还有一种方法说是用WMI,CSDN里面也有相关内容,但这个是哄人的,光一个架子,得不到数据。原因是WMI是通过SMBios来读DMI信息的,,微软在做WMI时可能参考了SMBios协议,认为硬件厂商会往DMI里面写信息,但“幼稚”的微软并没有想到“任性”的硬件厂商并没有这么做。。。所有传感器数据都是null。但WMI在获取硬件其它信息时还是很方便的。

提到的相关的Open Hardware Monitor,Winring0,WinI/O,都在我的下载里面。最后提供一张Console里面程序的运行画面。


------------------------------------------------------------------------------------------------------------------------------------

转自:http://item.congci.com/item/cpu-hexin-wendu-huoqu

最近在搞一个读取CPU温度的驱动,网上翻了好多资料,可发现全是copy的,原稿也就两三篇,可经实践发现其中不乏错误与片面,让人着实走弯路,燃起了我要总结一番的欲望。

这个驱动搞了一个多星期,总算可以运行了,测试了几台Intel和AMD的机器也都测试通过,测试对比用的是CPUID HWMonitor和Core Temp。

 

Intel和AMD的CPU中都有温度传感器(DTS),每个核心都有一个,温度就是由此获取来的,多核cpu可以使用 SetProcessAffinityMask API 来指定执行的CPU。

首先是利用CPUID来区分是Intel型号还是AMD型号,利用汇编和函数都可实现,考虑到64位系统不支持嵌入汇编,所以还是直接利用API函数就行。

CPUID其实就是对eax执行cpuid指令,返回信息储存在eax,ebx,ecx,edx中,令eax=0,可将CPU厂商信息返回在ebx,ecx,edx中,

    int CPUInfo[4];

    __cpuid(CPUInfo,0); 

Intel信息字符串为GenuineIntel,AMD为AuthenticAMD,只判断前4个字符就可以,只需与CPUInfo[1](ebx)比较就可得出型号。

 

接下来说如何获取温度,先从简单的说起,Intel实现起来比较简单:

  先以eax=0 执行 cpuid 检测 eax 支持的最大命令数,如果小于6就肯定不支持DTS。然后以eax=6 执行 cpuid,  然后测试 eax 第一位是否为1,如果为1表示CPU支持DTS。

   读取DTS:以 ecx=0x1A2 执行 rdmsr 指令, 测试 eax 的第30位是否为 1, 如果为 1 表示温度计算的初始值为 85 度否则表示从100度开始计算,这个值称为 Tjunction.

   eax=__readmsr(0x01A2)

 

      然后以 ecx=0x19c 执行 rdmsr 指令,  eax 的 16-23 位为表示当前DTS 值,当前温度要以下面公式计算.

 

       当前cpu温度 = Tjunction - DTS

 

       注意  signature 为 0x6f1, 0x6f0的 CPU DTS 值直接代表当前温度而不用Tjunction 相减. 而 signature 小于等于 0x6f4 的 Tjunction 一直为100。

 

AMD就比较恶心了,研究了挺长时间:

  AMD温度存储在NB寄存器中,这是一个热传感寄存器。AMD的CPU分为K8和K10,K8的温度存储在这个寄存器的23-14位,K10的在31-21位。

  要访问这个状态寄存器,需要对PCI进行读写。
先介绍俩个PCI用到的寄存器,CF8h和CFCh
CF8h: 存放配置空间的地址(CONFIG-ADDRESS)
CFCh: 保存配置空间的读写数据(CONFIG-DATA)
这两个空间对应于PCI桥路的两个寄存器,当桥路看到CPU在局部总线对这两个 I/O空间进行双字操作时,就将该I/O操作转变为PCI总线的配置操作。

温度读取                          

如果是K8的话,可以忽略低俩位,读取23-16就可以了,当然也可以读23-14,然后\4或者>>2;
如果是K10的话,那就读取31-21

如何判断K8,K10

__cpuid(CPUInfo,1); //cpuid执行1,取出eax
t=CPUInfo[0];

family=((t>>20)&0xFF) + ((t>>8)&0xF);
model=((t>>12)&0xF0) + ((t>>4)&0xF);
stepping=t&0xF;

如果Family ==0xf 而除了
               (((model == 4) && (stepping == 0)) ||
                    ((model == 5) && (stepping <= 1)))
则为K8
如果Family > 0xf,一般是G。那就是K10

温度的计算公式
K8 Temp = Value - 49'.   49这个值需要修正的:if (model >= 0x69 && model != 0xc1 && model != 0x6c && model != 0x7c)  temp=Value-49+21;
K10 Temp = Value / 8'.

IO访问PCI总线设备配置空间
配置空间地址寄存器的格式:
31      24 23         16 15            11 10              8 7               2 1   0
| reserve | bus number | device number | function number | register number | 0 | 1/0 |

所以知道 bus number, device number, function number, register number后,可以这么来构造配置空间地址寄存器
IOADDR = 0x80000000+bus*0x10000 +(device*8)*0x100 + uFunction&0x07 + register number&~3;
为什么需要0x80000000呢,因为
当CPU发出对I/O空间CFCh的操作时,PCI桥路将检查配置空间地址寄存器CF8h的31位。如果为1,就在PCI总线上产生一个相应的配置空

间读或写操作,0x80000000就是使配置空间地址寄存器为1。
经过上面的讨论后,可以写成
#define DeviceSlot(uDevice, uFunction) ((((uDevice)&0x1f)<<3)|((uFunction)&0x07))
#define GetDevice(uBus,uSlot,uAddress) (0x80000000L |((uBus&0xff)<<16)|(uSlot<<8)|(uAddress&~3));

这样知道 uBus, uDevice, uFunction, uAddress后就可以通过IO指令来读写了。

对于K8, uAddress为0xE4,对于K10 uAddress为0xA4

怎样获取uBus, uDevice, uFunction
从上面知道GetDevice需要 uBus, uDevice, uFunction的。
可以扫描PCI总线来获取,对于AMD K8来说,设备ID为0x1103,对于K10来说,设备ID为0x1203。 二者的uFunction都为3.
通过扫描PCI总线,匹配设备ID来获取。

BOOL get_bus_dev( int devieid,int *BUS, int *DEV ) //遍历PCI得到bus和dev
{

ULONG bus;
ULONG dev;
ULONG func=3; //K8 K10 fun为3
unsigned long Size;
PCI_COMMON_CONFIG PciConfig;
PCI_SLOT_NUMBER SlotNumber;

for(bus = 0; bus <= 255; ++bus) 
{
for(dev = 0; dev <= 31; ++dev) 
{
SlotNumber.u.AsULONG = 0;
SlotNumber.u.bits.DeviceNumber = dev;
SlotNumber.u.bits.FunctionNumber = func;
RtlZeroMemory(&PciConfig, sizeof(PCI_COMMON_CONFIG));

Size = HalGetBusData(PCIConfiguration,
bus,
SlotNumber.u.AsULONG,
&PciConfig,
PCI_COMMON_HDR_LENGTH); //API函数

if (Size==PCI_COMMON_HDR_LENGTH)
{
if ( devieid==PciConfig.DeviceID )
{
*BUS=bus;
*DEV=dev;
DbgPrint("BUS:%d \n",bus);
DbgPrint("DEV:%d \n",dev);
return TRUE;


}
}

return FALSE;
}

 

然后进行IO读写就可以获取温度了,K8:

static once =1;

if (once)
{
int bus,dev,slot;
if ( !get_bus_dev(0x1103,&bus,&dev) )
{
DbgPrint("获取BUS、DEV失败! \n");
return;
}

slot=DeviceSlot(dev,0x3);  //上面定义的宏
IO_ADDRE=GetDevice(bus,slot,0xE4);  //上面定义的宏

once=0;
}

_outpd(0xCF8,IO_ADDRE);//端口读写
CPUTemp=_inpd(0xCFC);//端口读写


CPUTemp=(CPUTemp>>16)&0xFF;
CPUTemp=CPUTemp - g_Offset;//g_Offset为49-21

DbgPrint("CPUTemp: %d \n",CPUTemp);

  • 0
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
获取CPU温度需要使用操作系统提供的API接口,而不是使用纯C语言编写的代码。下面给出两种常用的获取CPU温度的方法。 1. 使用Linux下的lm-sensors工具获取CPU温度 lm-sensors是Linux下的一个硬件监控工具,可以用来获取CPU温度等硬件信息。使用C语言调用lm-sensors的API接口可以获取CPU温度。下面是一个示例代码片段: ``` #include <stdio.h> #include <stdlib.h> #include <string.h> #define MAX_LINE_LEN 1024 int main() { char cmd[MAX_LINE_LEN] = "sensors | grep 'Core 0' | awk '{print $3}'"; char buf[MAX_LINE_LEN]; FILE *fp; fp = popen(cmd, "r"); fgets(buf, MAX_LINE_LEN, fp); pclose(fp); int temp = atoi(buf); printf("CPU temperature: %d°C\n", temp); return 0; } ``` 这段代码通过执行命令`sensors | grep 'Core 0' | awk '{print $3}'`获取CPU Core 0的温度,然后将结果转换为整数输出。 2. 使用Windows下的WMI获取CPU温度 在Windows系统下,可以使用WMI(Windows Management Instrumentation)获取CPU温度。WMI是Windows提供的一组API接口,可以用来获取系统信息。下面是一个示例代码片段: ``` #include <stdio.h> #include <windows.h> #include <wbemidl.h> #pragma comment(lib, "wbemuuid.lib") int main() { HRESULT hr; IWbemLocator *pLoc = NULL; IWbemServices *pSvc = NULL; IEnumWbemClassObject *pEnum = NULL; BSTR bstrQuery = NULL; BSTR bstrWQL = NULL; ULONG uReturn = 0; VARIANT vtProp; CIMTYPE cimtype; CoInitialize(NULL); hr = CoCreateInstance(CLSID_WbemLocator, 0, CLSCTX_INPROC_SERVER, IID_IWbemLocator, (LPVOID *)&pLoc); hr = pLoc->ConnectServer(_bstr_t(L"ROOT\\WMI"), NULL, NULL, 0, NULL, 0, 0, &pSvc); bstrWQL = SysAllocString(L"WQL"); bstrQuery = SysAllocString(L"SELECT * FROM MSAcpi_ThermalZoneTemperature"); hr = pSvc->ExecQuery(bstrWQL, bstrQuery, WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, NULL, &pEnum); hr = pEnum->Next(WBEM_INFINITE, 1, &vtProp, &uReturn); if (uReturn) { printf("CPU temperature: %d°C\n", (vtProp.lVal - 2732) / 10); VariantClear(&vtProp); } pEnum->Release(); pSvc->Release(); pLoc->Release(); CoUninitialize(); return 0; } ``` 这段代码使用WMI接口查询MSAcpi_ThermalZoneTemperature类的数据,然后将结果转换为整数输出。需要注意的是,由于WMI获取温度单位为0.1K,需要进行一定的计算才能转换为摄氏度。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值