I learned how to calculate CPU utilization when I started to use dzen from dzen’s gcpubar source.
Later, I read the man proc and knew more about it:
- /proc/stat
kernel/system statistics. Varies with architecture. Common entries include:
- cpu 3357 0 4313 1362393
The amount of time, measured in units of USER_HZ (1/100ths of a second on most architectures, use sysconf(_SC_CLK_TCK) to obtain the right value), that the system spent in user mode, user mode with low priority (nice), system mode, and the idle task, respectively.The last value should be USER_HZ times the second entry in the uptime pseudo-file.
In Linux 2.6 this line includes three additional columns: iowait - time waiting for I/O to complete (since 2.5.41); irq - time servicing interrupts (since 2.6.0-test4); softirq - time servicing softirqs (since 2.6.0-test4).
Since Linux 2.6.11, there is an eighth column, steal - stolen time, which is the time spent in other operating systems when running in a virtualized environment
Since Linux 2.6.24, there is a ninth column, guest, whichis the time spent running a virtual CPU for guest operating systems under the control of the Linux kernel.
It said the number is measured in units of USER_HZ and I assume that also applies on columns after fourth column. The easy way is to sum of all difference of two consecutive samples of those numbers. That sum represents the processed time between two sampling time. In which, the difference of column four is the idling time. If sum minus this idling time, that is the CPU utilization, divide it by sum, then it’s the percentage of CPU utilization.
Here is a sample of first few lines of /proc/stat:
cpu 972478 47 394033 6711061 136593 2572 9139 0 0 0
cpu0 487126 17 199314 3294038 89582 2572 8539 0 0 0
cpu1 485352 29 194719 3417023 47011 0 599 0 0 0
If you count the columns, there is a tenth column, I have no idea what tenth column means. It wasn’t mentioned in manpage.
Anyway, here is a sample code:
while :; do
# 0 1:user 2:unice 3:sys 4:idle 5:iowait 6:irq 7:softirq 8:steal 9:guest 10:?
ncpu=($(line</proc/stat))
sum="${ncpu[@]:1}"
cpu_total=$((${sum// /+}))
cpu_maxval=$((cpu_total - ocpu_total))
cpu_val=$((cpu_maxval - (ncpu[4]-ocpu[4])))
cpu_percentage=$((100 * cpu_val / cpu_maxval))
ocpu=("${ncpu[@]}")
ocpu_total=$cpu_total
echo "$cpu_percentage%"
sleep 1
done
There are few tricky things, I used line to grab first line since I only need to know the total percentage of all process units. After that, it is fed to Bash array ncpu initialization. Pretty neat, isn’t it?
Then, the code assigns sum with a string representation of ncpu of elements from second to last. The first one is "cpu", we don’t need that. Replacing all spaces with plus sign, so it will read like 123+456+.... Feed it to arithmetic expansion, we get the sum of all numbers. Minus it with previous sum, that is sum of difference of two samples, cpu_maxval.
Then, minus the difference of idling time, it’s the utilization time, cpu_val. Let cpu_val be divided by cpu_maxval, we get the percentage. Job is done.
I had to check the kernel source directly to find out what the last few columns were:
ReplyDeleteseq_printf(p, "cpu %llu %llu %llu %llu %llu %llu %llu %llu %llu %llu\n",
(unsigned long long)cputime64_to_clock_t(user),
(unsigned long long)cputime64_to_clock_t(nice),
(unsigned long long)cputime64_to_clock_t(system),
(unsigned long long)cputime64_to_clock_t(idle),
(unsigned long long)cputime64_to_clock_t(iowait),
(unsigned long long)cputime64_to_clock_t(irq),
(unsigned long long)cputime64_to_clock_t(softirq),
(unsigned long long)cputime64_to_clock_t(steal),
(unsigned long long)cputime64_to_clock_t(guest),
(unsigned long long)cputime64_to_clock_t(guest_nice));