htop
is an enhancement over regular top
and it's a very popular tool. But did you ever ask yourself how it actually works? In this article we'll be looking at where htop
gets cpu and memory utilization information from. Given that htop
runs on many different platforms, we'll be discussing just the Linux version of the story.
Cpu utilization per cpu
htop
displays cpu utilization for each cpu. This is one the most key things we use htop
for, in order to gauge the current load on the system.
The information comes from /proc/stat
(documented here). This file contains a few different counters, but what's of interest to us right here are just the cpu lines which look like this:
cpu 5183484 9992 1575742 162186539 903310 0 27048 0 0 0
cpu0 1355329 2304 389040 40426679 299055 0 6431 0 0 0
cpu1 1234845 2602 423662 40594393 187209 0 16487 0 0 0
cpu2 1347723 2837 413561 40442958 239035 0 4085 0 0 0
cpu3 1245586 2246 349478 40722507 178009 0 44 0 0 0
So what are these numbers? Well, each number represents the amount of time spent in a particular state, by that cpu. This is true for each line that begins with cpuN. So cpu0 (which in htop
is displayed as cpu #1) spent:
- 1355329 units of time in user mode (ie. running user processes)
- 2304 units of time in nice mode (ie. running user processes with a nice setting)
- 389040 units of time in system mode (ie. running kernel processes)
- 40426679 units of time in idle mode (ie. not doing anything)
- 299055 units of time in iowait (ie. waiting for io to become ready)
- 0 units of time servicing interrupts
- 6431 units of time servicing soft interrupts
- 0 units of time where the VM guest was waiting for the host CPU (if we're running in a VM)
- 0 units of time where we're running a VM guest
- 0 units of time where the VM guest is running with a nice setting
The first line in the file is simply an aggregate of all the per-cpu lines.
This is effectively the granularity that the kernel gives us about what the cpu spent time doing. The unit is something called USER_HZ
, which is 100 on this system. So if we spent 1,355,329 units in user mode, that means 1355329 / 100 = 13,553 seconds (3.76 hours) spent running user processes since the system booted. By contrast, we spent 4.67 days in idle time, so this is clearly not a system under sustained load.
So how does htop
use this? Each time it updates the ui it reads the contents of /proc/stat
. Here are two subsequent readings one second apart, which show just the values for cpu0:
# time=0s
cpu0 1366294 2305 392684 40566185 300222 0 6590 0 0 0
# time=1s
cpu0 1366296 2305 392684 40566283 300222 0 6590 0 0 0
# compute the delta between the readings
cpu0 2 0 0 98 0 0 0 0 0 0
We can see that between the first and the second reading we spent 2 units in user mode and 98 units in idle mode. If we add up all of the numbers (2 + 98 = 100) we can see that the cpu spent 2 / 100 = 2% of its time running user processes, which means cpu utilization for cpu0 would be displayed as 2%.
Cpu utilization per process
Cpu utilization per process is actually measured in a very similar way. This time the file being read is /proc/<pid>/stat
(documented here) which contains a whole bunch of counters about the process in question. Here it is for the X server:
939 (Xorg) S 904 939 939 1025 939 4194560 233020 15663 1847 225 297398 280532 25 14 20 0 10 0 4719 789872640 10552 18446744073709551615 93843303677952 93843305320677 140727447720048 0 0 0 0 4096 1098933999 0 0 0 17 3 0 0 3235 0 0 93843305821872 93843305878768 93843309584384 140727447727751 140727447727899 140727447727899 140727447728101 0
Fields 14 and 15 are the ones we are looking for here, because they represent respectively:
- utime, or the amount of time this process has been scheduled in user mode
- stime, or the amount of time this process has been scheduled in kernel mode
These numbers are measured in the same unit we've seen before, namely USER_HZ
. htop
will thus calculate the cpu utilization per process as: ((utime + stime) - (utime_prev + stime_prev)) / USER_HZ
.
htop
will calculate this for every running process each time it updates.
Memory utilization on the system
htop displays memory utilization in terms of physical memory and swap space.
This information is read from /proc/meminfo
(documented here) which looks like this (here showing just the lines that htop
cares about):
MemTotal: 3723752 kB
MemFree: 180308 kB
MemAvailable: 558240 kB
Buffers: 66816 kB
Cached: 782608 kB
SReclaimable: 87904 kB
SwapTotal: 1003516 kB
SwapCached: 13348 kB
SwapFree: 317256 kB
Shmem: 313436 kB
Unlike the cpu stats, these are not counters that accumulate over time, they are point in time snapshots. Calculating the current memory utilization comes down to MemTotal
- MemFree
. Likewise, calculating swap usage means SwapTotal
- SwapFree
- SwapCached
.
htop
uses the other components of memory use (buffers, cached, shared mem) to color parts of the progress bar accordingly.
Memory utilization per process
Memory utilization per process is shown as four numbers:
- virtual memory, ie. how much memory the process has allocated (but not necessarily used yet)
- resident memory, ie. how much memory the process currently uses
- shared memory, ie. how much of its resident memory is composed of shared libraries that other processes are using
- memory utilization %, ie. how much of physical memory this process is using. This is based on the resident memory number.
This information is read from the file /proc/<pid>/statm
(documented here). The file looks like this:
196790 9938 4488 402 0 28226 0
This is the X server process once again, and the numbers mean:
- virtual memory size
- resident memory size
- shared memory size
- size of the program code (binary code)
- unused
- size of the data + stack of the program
- unused
These numbers are in terms of the page size, which on this system is 4096. So to calculate the resident memory for Xorg htop
does 9938 * 4096 = 38mb
. To calculate the percentage of system memory this process uses htop
does (9938 * 4096) / (3723752 * 1024) = 1.1%
using the MemTotal
number from before.
Conclusion
As we have seen in this practical example the kernel provides super useful information through a file based interface. These are not really files per se, because it's just in-memory state inside the kernel made available through the file system. So there is minimal overhead associated with opening/reading/closing these files. And this interface makes it very accessible to both sophisticated programs like htop
as well as simple scripts to access, because there is no need to link against system libraries. Arguably, this API makes the information more discoverable because any user on the system can cat
files on the /proc
file system to see what they contain.
The downside is that these are files in text format which have to be parsed. If the format of the file changes over time the parsing logic may break, and a program like htop
has to account for the fact that newer kernel versions may add additional fields. In Linux there is also an evolution in progress where the /proc
file system remains, but more and more information is exposed through the /sys
file system.