CPU温度监测发展历程和硬件支持
- 早期的CPU(2000以前),都是采用主板CPU插槽下面的温度探头来测量温度,因此准确性欠佳
- 到了2000以后,CPU开始逐步内置温度传感器。早期的CPU温度传感器的信息,是由CPU汇报给BIOS,通过WMI来获取,由于WMI只是操作系统层面的东西,所以准确性以及时效性都很差。此时的CPU温度数据一旦变化,必须要等到系统某些信息发生变化时数据才会刷新。 所以后来硬件默认放弃了往WMI里面写数据,现在通过wmi基本获取不到温度信息了。
- 再后来CPU制造商开始向CPU内加入DTS(Digital Thermal Sensor,数字温度传感器),所得的数据更为精确。(Intel是从Yonah核心的P-M处理器开始使用DTS的,官方文档里面有说明,而AMD官方确认DTS的存在,是从修订版本为F的Opteron )。
- DTS的工作原理是:Absolute Core Temperature = TJMax - DTS(实际温度=TJMax-DTS),Tjmax有固定和从寄存器读取两种方式。但由于每个CPU的TJMax值也肯定完全不同,CPU厂商不可能在每颗CPU出厂之前都进行测试和校正,只能根据ES版CPU来制定一个大概的TJMax值。 这些说明我们实际获取到的CPU温度不是很准确
获取CPU温度使用到的技术
- DeviceIoControl 函数是直接发送控制代码到指定的设备驱动程序,使相应的移动设备以执行相应的操作的函数。
- drivers.sys 底层驱动程序,主要目的是获取Ring0权限,为了能够无提供给开发者使用,需要做一个DLL提供对外的接口(mydrivers.dll)
- mydrivers.dll 加载mydrivers.sys与系统驱动层进行通讯,执行汇编指令,读写寄存器
- intel cpu 所有系列的CPU都是统一的使用用rdmsr指令读取特定寄存器的值,然后用TjunctionMax 减去这个值就是当前cpu的温度
- amd cpu,这个cpu分为10,16,17这三个系列,每个系列的对应的温度获取方式不一样
CPU温度获取的具体实现方式
- intel的DST的值就存放在2个寄存器里面:0x019C、0x1B1,读取出来后当前温度 = TJMax-dst。实现代码如下:
void IntelCPU::GetTemperature(void) {DWORD eax = 0, edx = 0, ebx = 0;DWORD dwMax = 100;float fValue = 0.0;if (!m_bInit){GetCPUFamily();GetCPUCoreCount();m_bInit = true;}if(Rdmsr(IA32_TEMPERATURE_TARGET, &eax, &edx)){dwMax = (eax >> 16) & 0xff;}eax = 0;edx = 0;switch(m_CPUFamily){case 0x06:switch(m_CPUModel){case 0x0F:switch(m_CPUStepping){case 0x06:switch(m_CPUCore){case 2:dwMax = 80 + 10;break;case 4:dwMax = 90 + 10;break;default:dwMax = 85 + 10;break;}dwMax = 80 + 10;break;case 0x0B:dwMax = 90 + 10;break;case 0x0D:dwMax = 85 + 10;break;default:dwMax = 85 + 10;break;}break;case 0x17:dwMax = 100;case 0x1C:switch(m_CPUStepping){case 0x02:dwMax = 90;break;case 0x0A:dwMax = 100;break;default:dwMax = 90;break;}break;case 0x1A:case 0x1E:case 0x25:case 0x2c:dwMax = 100;}}if(WinRing0::RdmsrEx(IA32_THERM_STATUS_MSR, &eax, &ebx, (1L << 0))){if((eax & 0x80000000) != 0){float deltaT = (float)((eax & 0x007F0000) >> 16);m_Temperature = (float)dwMax - deltaT;}}else if(WinRing0::RdmsrEx(IA32_PACKAGE_THERM_STATUS, &eax, &ebx, (1L << 0))){if((eax & 0x80000000) != 0){float deltaT = (float)((eax & 0x007F0000) >> 16);m_Temperature = (float)dwMax - deltaT;}} }
-
amd10系列温度获取,温度存储的寄存器有多个:0x1203、0x1303、0x1703、0x1603,分别进行读取
void AMD10CPU::GetTemperature(void) {DWORD pciAddress = 0;int nFamily = 10;pciAddress = CCPUBase::GetPciAddress(MISCELLANEOUS_CONTROL_FUNCTION, PCI_AMD_10H_MISCELLANEOUS_DEVICE_ID);if(pciAddress == 0){pciAddress = CCPUBase::GetPciAddress(MISCELLANEOUS_CONTROL_FUNCTION, PCI_AMD_11H_MISCELLANEOUS_DEVICE_ID);nFamily = 11;}if(pciAddress == 0){pciAddress = CCPUBase::GetPciAddress(MISCELLANEOUS_CONTROL_FUNCTION, FAMILY_12H_14H_MISCELLANEOUS_CONTROL_DEVICE_ID);nFamily = 12;}if(pciAddress == 0){pciAddress = CCPUBase::GetPciAddress(MISCELLANEOUS_CONTROL_FUNCTION, FAMILY_15H_MISCELLANEOUS_CONTROL_DEVICE_ID);nFamily = 15;}if(pciAddress != 0){DWORD value;if(WinRing0::ReadPciConfigDwordEx(pciAddress, REPORTED_TEMPERATURE_CONTROL_REGISTER, &value)){if(nFamily == 15 && (value & 0x30000) == 0x30000){m_Temperature = ((value >> 21) & 0x7FC) / 8.0f - 49;}else{m_Temperature = ((value >> 21) & 0x7FF) / 8.0f;}}} }
-
amd16系列温度获取,需要先读取到DST的地址再来读取dst值
void AMD0FCPU::GetTemperature(void) {DWORD value;pciAddress = WinRing0::FindPciDeviceById(PCI_AMD_VENDOR_ID, PCI_AMD_0FH_MISCELLANEOUS_DEVICE_ID, 0);if(pciAddress != 0xFFFFFFFF){if(WinRing0::WritePciConfigDwordEx(pciAddress, THERMTRIP_STATUS_REGISTER, THERM_SENSE_CORE_SEL_CPU0)){if(WinRing0::ReadPciConfigDwordEx(pciAddress, THERMTRIP_STATUS_REGISTER, &value)){m_Temperature = (float)((value >> 16) & 0xFF);}}} }
-
amd17系列温度获取:
void AMD17CPU::GetTemperature(void) {WinRing0Ins.WaitIsaBusMutex();if (!WinRing0::WritePciConfigDwordEx(0, WRITE_TEMPERATURE_CONTROL_REGISTER, FAMILY_17H_M01H_THM_TCON_TEMP)){m_Temperature = 0;WinRing0Ins.ReleaseIsaBusMutex();return;}DWORD value = 0;if (WinRing0::ReadPciConfigDwordEx(0, READ_TEMPERATURE_CONTROL_REGISTER, &value)){m_Temperature = ((value >> 21) & 0x7FF) / 8.0f;if ((value & FAMILY_17H_M01H_THM_TCON_TEMP_RANGE_SEL) != 0)m_Temperature -= 49;}m_Temperature -= m_tctlOffset;WinRing0Ins.ReleaseIsaBusMutex(); }
最后本人写了一个demo来获取硬件温度,https://download.csdn.net/download/dm569263708/87360682