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/download/dm569263708/87360682
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/download/dm569263708/87360682