Finding Linux Processor Temperatures

Packages like "xsensors" and "lm_sensors" can show you the temperature of your processor's core(s). And that's great - right up until you want to write a program to retrieve the temperature yourself. A direct look at the processor core temperatures is available in /sys/devices/platform/coretemp.0/hwmon/hwmon?/ with the temp?_input files ... at least, this seems to be true on an Intel-based system. I can't speak to AMD-based systems, but I know those directories don't exist on a Raspberry Pi 4. So I looked for a more generalized solution.

This can be found in /sys/class/thermal/, which contains one or several thermal_zone? directory links. Each of these contain ... more stuff. I say this because what's in those varies considerably. The most obvious, and important to this conversation, is the temp file which includes the current temperature. But since there are several "thermal zones," you should be asking yourself "temperature of what?" Let's look at thermal_zone0 on my main laptop:

$ cd /sys/class/thermal/
$ ls
cooling_device0@   cooling_device3@  cooling_device7@  thermal_zone1@  thermal_zone5@
cooling_device1@   cooling_device4@  cooling_device8@  thermal_zone2@  thermal_zone6@
cooling_device10@  cooling_device5@  cooling_device9@  thermal_zone3@
cooling_device2@   cooling_device6@  thermal_zone0@    thermal_zone4@
$ cd thermal_zone0/
$ ls
available_policies  k_d   mode    slope              trip_point_0_temp
device@             k_i   offset  subsystem@         trip_point_0_type
hwmon1/             k_po  policy  sustainable_power  type
integral_cutoff     k_pu  power/  temp               uevent
$ cat temp
44000
$ cat type
acpitz

The temperature is in Celsius ... or is that milliCelsius given that it needs to be divided by 1000 to get a proper value? temp and type seem to always be present in these thermal_zone? folders? Let's try to look at all of them.

$ cd /sys/class/thermal/
$ for folder in thermal_zone* ; do cat ${folder}/type ; cat ${folder}/temp ; echo ; done
acpitz
44000

INT3400 Thermal
20000

SEN1
50

pch_skylake
37000

B0D4
43000

x86_pkg_temp
43000

iwlwifi_1
cat: thermal_zone6/temp: No data available

That last partially blows my idea that these two items are always present: thermal_zone6 has a temp file, but ... nothing in it? And notice that whatever "SEN1" is, it isn't following the "multiplied-by-1000" rule. Not only that, checking these values multiple times has told me that the SEN1 and "INT3400 Thermal" values are both fixed: they never change.

Results are quite different on a Raspberry Pi (4):

$ cd /sys/class/thermal/
$ for folder in thermal_zone* ; do cat ${folder}/type ; cat ${folder}/temp ; echo ; done
cpu-thermal
45277

There's only one thermal_zone? folder, and the type is different.

The best explanation I've found of what all these "types" mean comes from https://askubuntu.com/questions/1110943/what-do-the-different-thermal-zones-actually-correspond-to - and it's less helpful than one might hope:

The exact definition of what a given thermal zone represents is defined by
the driver for the given zone. Different processors and motherboards make
different thermometers available to linux, and so every one has it's own
name. Each of the different zones is a different thermometer on the system:
the "acpitz" one is the one made available through ACPI, and the
x86_pkg_temp is the temperature exported by the core x86 spec. The ACPI one
is a motherboard sensor that is near the CPU socket, and the x86_pkg_temp
is within the CPU itself.

Based on a git grep through the kernel source tree, the 2nd one
(pch_cannonlake) specifies the thermomemter output of an intel-specific
thermocouple, used for thermal throttling. However, determining it's exact
location (as with the others) is at best an educated guess: it will vary by
chipset and manufacturer, so you would need to consult the detailed
specification and layout of your chip.

In practical terms, what this means for a weekend coder like myself is that I'm going to parse the type files looking for "cpu-thermal" or "x86_pkg_temp" and use only values that match one of those. And it appears that the /sys/class/thermal/ folder doesn't exist on VMs, so that's something to test before proceeding to look for temperatures.

[As a further compatibility note: WSL2 has the /sys/class/thermal/ folder, but there are no thermal_zone files in there - so no compatibility layer, and this stuff doesn't work.]

Here's the Python version I have so far, somewhat fancier than the Bash version:

#!/usr/bin/env python3
import os
import glob

globThermalDirs = '/sys/class/thermal/thermal_zone*'
maxTemp = 0
try:
    zoneDirs = glob.glob(globThermalDirs)
    for tdir in zoneDirs:
        filenameE = tdir + "/temp"
        filenameY = tdir + "/type"
        with open(filenameE) as fe, open(filenameY) as fy:
            try:
                tempType = fy.read().splitlines()[0]
                rawTemp  = float(fe.read().splitlines()[0])
                if rawTemp > 1000 :
                    zoneTemp = float(rawTemp)/1000
                else:
                    zoneTemp = rawTemp
                if tempType == "cpu-thermal" or tempType == "x86_pkg_temp" :
                    notification = " ***"
                else:
                    notification = ""
                print(filenameE + "(" + tempType + "): " + str(zoneTemp) + notification)
            except OSError:
                print("OSError on " + filenameE);
            if (zoneTemp > maxTemp):
                maxTemp = zoneTemp
    print("Max temp found: " + str(maxTemp))
except:
    print("Failed to find '" + globThermalDirs + "'")

The next step is to trim the output down to a single number. When I started working on this, I thought the best thing would be to export the highest temperature. You can see this in the maxTemp variable. But now I think it's better to go with "cpu-thermal" or "x86_pkg_temp" and drop the rest. Here's the current output (from my main laptop, which is Intel-based):

/sys/class/thermal/thermal_zone2/temp(SEN1): 50.0
/sys/class/thermal/thermal_zone0/temp(acpitz): 41.0
/sys/class/thermal/thermal_zone5/temp(x86_pkg_temp): 43.0 ***
/sys/class/thermal/thermal_zone3/temp(pch_skylake): 36.5
/sys/class/thermal/thermal_zone1/temp(INT3400 Thermal): 20.0
OSError on /sys/class/thermal/thermal_zone6/temp
/sys/class/thermal/thermal_zone4/temp(B0D4): 43.0
Max temp found: 50.0