windows获取CPU温度

CPU温度监测发展历程和硬件支持

  1. 早期的CPU(2000以前),都是采用主板CPU插槽下面的温度探头来测量温度,因此准确性欠佳
  2. 到了2000以后,CPU开始逐步内置温度传感器。早期的CPU温度传感器的信息,是由CPU汇报给BIOS,通过WMI来获取,由于WMI只是操作系统层面的东西,所以准确性以及时效性都很差。此时的CPU温度数据一旦变化,必须要等到系统某些信息发生变化时数据才会刷新。 所以后来硬件默认放弃了往WMI里面写数据,现在通过wmi基本获取不到温度信息了。
  3. 再后来CPU制造商开始向CPU内加入DTS(Digital Thermal Sensor,数字温度传感器),所得的数据更为精确。(Intel是从Yonah核心的P-M处理器开始使用DTS的,官方文档里面有说明,而AMD官方确认DTS的存在,是从修订版本为F的Opteron )。
  4. DTS的工作原理是:Absolute Core Temperature = TJMax - DTS(实际温度=TJMax-DTS),Tjmax有固定和从寄存器读取两种方式。但由于每个CPU的TJMax值也肯定完全不同,CPU厂商不可能在每颗CPU出厂之前都进行测试和校正,只能根据ES版CPU来制定一个大概的TJMax值。 这些说明我们实际获取到的CPU温度不是很准确

获取CPU温度使用到的技术

  1. DeviceIoControl 函数是直接发送控制代码到指定的设备驱动程序,使相应的移动设备以执行相应的操作的函数。
  2. drivers.sys 底层驱动程序,主要目的是获取Ring0权限,为了能够无提供给开发者使用,需要做一个DLL提供对外的接口(mydrivers.dll)
  3. mydrivers.dll 加载mydrivers.sys与系统驱动层进行通讯,执行汇编指令,读写寄存器
  4. intel cpu 所有系列的CPU都是统一的使用用rdmsr指令读取特定寄存器的值,然后用TjunctionMax 减去这个值就是当前cpu的温度
  5. amd cpu,这个cpu分为10,16,17这三个系列,每个系列的对应的温度获取方式不一样

 CPU温度获取的具体实现方式

  1. 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;
                    }
            }
    }

  2. 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;
                        }
                    }
            }
    }

  3. 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);
                            }
                    }
            }
    }

  4. 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

猜你喜欢

转载自blog.csdn.net/dm569263708/article/details/127089457
今日推荐