【问题标题】:How to access CPU's heat sensors?如何访问 CPU 的热传感器?
【发布时间】:2015-11-12 15:59:24
【问题描述】:

我正在开发需要访问 CPU 中的温度传感器并控制它们的软件。

我不太了解硬件接口;我只知道如何与鼠标交互。我用谷歌搜索了很多,但没有找到任何相关信息或代码。

我真的需要在我的软件中添加这个。请指导我如何使用 C 或 C++ 或 ASM 控制传感器。

【问题讨论】:

    标签: c windows hardware sensors hardware-interface


    【解决方案1】:

    如果没有特定的内核驱动程序,除了通过 WMI 之外,很难查询温度。这是一段基于 WMI 的 MSAcpi_ThermalZoneTemperature 类的 C 代码:

    HRESULT GetCpuTemperature(LPLONG pTemperature)
    {
        if (pTemperature == NULL)
            return E_INVALIDARG;
    
        *pTemperature = -1;
        HRESULT ci = CoInitialize(NULL); // needs comdef.h
        HRESULT hr = CoInitializeSecurity(NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_DEFAULT, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE, NULL);
        if (SUCCEEDED(hr))
        {
            IWbemLocator *pLocator; // needs Wbemidl.h & Wbemuuid.lib
            hr = CoCreateInstance(CLSID_WbemAdministrativeLocator, NULL, CLSCTX_INPROC_SERVER, IID_IWbemLocator, (LPVOID*)&pLocator);
            if (SUCCEEDED(hr))
            {
                IWbemServices *pServices;
                BSTR ns = SysAllocString(L"root\\WMI");
                hr = pLocator->ConnectServer(ns, NULL, NULL, NULL, 0, NULL, NULL, &pServices);
                pLocator->Release();
                SysFreeString(ns);
                if (SUCCEEDED(hr))
                {
                    BSTR query = SysAllocString(L"SELECT * FROM MSAcpi_ThermalZoneTemperature");
                    BSTR wql = SysAllocString(L"WQL");
                    IEnumWbemClassObject *pEnum;
                    hr = pServices->ExecQuery(wql, query, WBEM_FLAG_RETURN_IMMEDIATELY | WBEM_FLAG_FORWARD_ONLY, NULL, &pEnum);
                    SysFreeString(wql);
                    SysFreeString(query);
                    pServices->Release();
                    if (SUCCEEDED(hr))
                    {
                        IWbemClassObject *pObject;
                        ULONG returned;
                        hr = pEnum->Next(WBEM_INFINITE, 1, &pObject, &returned);
                        pEnum->Release();
                        if (SUCCEEDED(hr))
                        {
                            BSTR temp = SysAllocString(L"CurrentTemperature");
                            VARIANT v;
                            VariantInit(&v);
                            hr = pObject->Get(temp, 0, &v, NULL, NULL);
                            pObject->Release();
                            SysFreeString(temp);
                            if (SUCCEEDED(hr))
                            {
                                *pTemperature = V_I4(&v);
                            }
                            VariantClear(&v);
                        }
                    }
                }
                if (ci == S_OK)
                {
                    CoUninitialize();
                }
            }
        }
        return hr;
    }
    

    还有一些测试代码:

    HRESULT GetCpuTemperature(LPLONG pTemperature);
    
    int _tmain(int argc, _TCHAR* argv[])
    {
        LONG temp;
        HRESULT hr = GetCpuTemperature(&temp);
        printf("hr=0x%08x temp=%i\n", hr, temp);
    }
    

    【讨论】:

    • 几乎没有系统驱动程序实现这个 WMI 类,这真的很可惜,因为这意味着你必须在大部分时间编写自己的内核模式驱动程序。
    • 两个问题:(1) 出于某种原因,当我调用此 WMI 方法时(来自非提升进程)IEnumWbemClassObject::Next() 总是返回 80041003。并且(2)即使我调用这个过程提升了 WMI 调用成功但返回的值永远不会改变,假设一个输出它是这样的:((double)temp / 10 - 273.15)。我收到27.85 这是错误的。知道为什么吗?
    • @c00000fd - 0x80041003 显然是 WBEM_E_ACCESS_DENIED。对于 2) MSAcpi_ThermalZoneTemperature 严格读取“热区”温度,而不是 CPU,因此可能有点不正确。这很大程度上取决于硬件/供应商/驱动程序......为了准确阅读,您需要一个内核驱动程序,正如其他指出的那样。这里有一个很棒的开源项目:github.com/openhardwaremonitor/openhardwaremonitor,它拥有你需要的一切(没有魔法,在每种情况下都需要做很多工作)
    • @SimonMourier:谢谢。哇。说到不必要的权限提升——我是否“真的”需要将我的代码作为内核驱动程序运行只是为了获得 CPU 温度。反正。我很好奇,在您发布的那个库中,他们使用CreateServicedwServiceType 安装驱动程序设置为SERVICE_KERNEL_DRIVER。要分发此类代码,我是否还需要使用Extended Validation 数字证书对该服务进行签名?
    • @c00000fd - 如果你想公开发布内核驱动程序,是的,你必须通过一个特定的进程 msdn.microsoft.com/en-us/windows/hardware/drivers/dashboard/… ,唉,不仅仅是 EV 签名。 Microsoft 已将其落实到位,以确保当 Windows 蓝屏时,我们知道这是谁的错。
    【解决方案2】:

    我假设您对 IA-32(英特尔架构,32 位)CPU 和 Microsoft Windows 感兴趣。

    型号特定寄存器 (MSR) IA32_THERM_STATUS 有 7 位编码“数字读数(位 22:16,RO)- 相对于 TCC 激活温度的 1 摄氏度的数字温度读数。” (请参阅“英特尔® 64 和 IA-32 架构 - 软件开发人员手册 - 第 3 卷(3A 和 3B):系统编程指南”http://www.intel.com/Assets/PDF/manual/325384.pdf 中的“14.5.5.2 读取数字传感器”)。

    所以IA32_THERM_STATUS 不会给你“CPU 温度”,而是它的一些代理。

    为了读取IA32_THERM_STATUS寄存器,你使用asm指令rdmsr,现在rdmsr不能从用户空间代码调用,所以你需要一些内核空间代码(也许是设备驱动程序?)。

    你也可以使用内在的__readmsr(见http://msdn.microsoft.com/en-us/library/y55zyfdx(v=VS.100).aspx),它有同样的限制:“这个函数只在内核模式下可用”。

    每个 CPU 内核都有自己的数字热传感器 (DTS),因此需要更多代码来获取所有温度(也许使用关联掩码?请参阅 Win32 API SetThreadAffinityMask)。

    我做了一些测试,实际上发现IA32_THERM_STATUS DTS 读数和 Prime95“就地大型 FFT(最大热量、功耗、一些 RAM 测试)”测试之间存在相关性。 Prime95 是ftp://mersenne.org/gimps/p95v266.zip

    我没有找到从 DTS 读数中获取“CPU 温度”(无论是什么意思)的公式。

    编辑:

    引用"fgw" 的一篇有趣的帖子TJunction Max? #THERMTRIP? #PROCHOT?(2007 年 12 月):

    没有办法在任何寄存器中找到某个处理器的 tjmax。 因此没有软件可以读取这个值。各种软件开发商 正在做,是他们只是假设某个特定的连接 处理器并将此信息保存在程序内的表中。 除此之外,tjmax 甚至不是他们所追求的正确值。在 事实上,他们正在寻找 TCC 激活温度阈值。这个 温度阈值用于计算当前绝对值 核心温度从。理论上你可以说:绝对 coretemperature = TCC activacion 温度阈值 - DTS 我必须 理论上说是因为,如上所述,这个 TCC activacion 温度阈值不能由软件读取,必须假设 由程序员。在大多数情况下(coretemp、everest,...),它们 假设 85C 或 100C 的值取决于处理器系列和 修订。因为此 TCC 激活温度阈值已校准 在每个处理器单独制造期间,它可能是 83C 一个处理器,但另一个处理器可能是 87C。考虑到 这些程序计算核心温度的方式,你可以计算 自己动手,绝对核心温度有多准确!两者都不 tjmax 也不是“最想要的”TCC 激活温度阈值 在任何公开的英特尔文件中都可以找到。经过一些讨论 在英特尔开发者论坛上,英特尔没有表现出这样做的迹象 可用的信息。

    【讨论】:

    • 我可以改变它的值吗?
    • @Akito 你的意思是写到寄存器IA32_THERM_STATUS?目的是什么?
    • 我正在尝试对硬件安全漏洞进行研究,所以我想知道您是否可以以某种方式更改测量的温度值,以便计算机可能会关闭。
    • @Akito 您可以查看英特尔的手册。在我看来,您可以写入注册表的某些位。
    • 抱歉恢复旧线程。 TJ MAX 可以在 MSR IA32_TEMPERATURE_TARGET 中找到。有关详细信息,请参阅:intel.com/content/www/us/en/embedded/testing-and-validation/… 但是我不知道不同的英特尔处理器系​​列是否支持该 MSR。
    【解决方案3】:

    您可以从 WMI 中的 MSAcpi_ThermalZoneTemperature 中读取它

    在 C++ 中使用 WMI 有点复杂,请参阅 MSDN explanantion and examples

    注意:更改了原来无用的答案

    【讨论】:

    • 抱歉 - 是的,它只是一些专有 dll 的包装器。
    【解决方案4】:

    这可能取决于操作系统。在 GNU/Linux 上,它与 ACPI 有关。有些硬件甚至没有测量温度的物理设备。

    【讨论】:

    • 这就是我提到Windows的原因。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-05-11
    • 1970-01-01
    • 2021-06-24
    相关资源
    最近更新 更多