6

I'm writing a bash script to print system stats to my dwm status bar using xsetroot. Everything works as expected. What I'm currently missing is an easy way that just uses standard *nix tools to give me the current load for every core on my system (I have 4 cores.). I can't figure out how to do this e.g. using top. All other posts I found on this site so far just deal with average load. Has anybody done this before?

The main reason that I want it for every single core is to have a cheap and rough tool to check if a program is running some code I wrote in parallel (e.g. a for each loop).

1
  • watch /proc/stat, I don't offhand know what all of those numbers translate to, but man procfs probably does.
    – mikeserv
    Aug 31, 2014 at 2:13

4 Answers 4

8

This creates a bash array whose elements are the loads for each CPU:

loads=($(mpstat -P ALL 1 1 | awk '/Average:/ && $2 ~ /[0-9]/ {print $3}'))

Since bash arrays are numbered starting with zero, the load of the second CPU would be printed with:

echo ${loads[1]}

This requires the utility mpstat. To install it on a debian-like system, run:

apt-get install sysstat

How it works

The somewhat verbose output produced by mpstat looks like:

$ mpstat -P ALL 1 1
Linux 3.2.0-4-amd64 (MyMachine)     08/30/2014      _x86_64_        (2 CPU)

10:12:35 PM  CPU    %usr   %nice    %sys %iowait    %irq   %soft  %steal  %guest   %idle
10:12:36 PM  all    1.49    0.00    1.49    0.00    0.00    0.00    0.00    0.00   97.01
10:12:36 PM    0    0.00    0.00    2.02    0.00    0.00    0.00    0.00    0.00   97.98
10:12:36 PM    1    1.96    0.00    1.96    0.00    0.00    0.00    0.00    0.00   96.08

Average:     CPU    %usr   %nice    %sys %iowait    %irq   %soft  %steal  %guest   %idle
Average:     all    1.49    0.00    1.49    0.00    0.00    0.00    0.00    0.00   97.01
Average:       0    0.00    0.00    2.02    0.00    0.00    0.00    0.00    0.00   97.98
Average:       1    1.96    0.00    1.96    0.00    0.00    0.00    0.00    0.00   96.08

where -P ALL tells mpstat to show all cpus and the arguments 1 1 tell it to print output every second and stop after the first second.

To select just the values that we want, this awk command is used:

awk '/Average:/ && $2 ~ /[0-9]/ {print $3}'

This selects only the final lines (the ones starting with Average: and, among those, only select those whose second column is numeric. For those lines, the third column (cpu load) is printed.

Because of the use of parentheses, the output from the mpstat-awk pipeline is captured into a bash array.

4

Calculating the average per core usage from /proc/stat

The best solution I have come up so far uses bc to account for floating point arithmetic:

# Calculate average cpu usage per core.
#      user  nice system   idle iowait irq softirq steal guest guest_nice
# cpu0 30404 2382   6277 554768   6061   0      19    0      0          0
A=($(sed -n '2,5p' /proc/stat))
# user         + nice     + system   + idle
B0=$((${A[1]}  + ${A[2]}  + ${A[3]}  + ${A[4]}))
B1=$((${A[12]} + ${A[13]} + ${A[14]} + ${A[15]}))
B2=$((${A[23]} + ${A[24]} + ${A[25]} + ${A[26]}))
B3=$((${A[34]} + ${A[35]} + ${A[36]} + ${A[37]}))
sleep 2
# user         + nice     + system   + idle
C=($(sed -n '2,5p' /proc/stat))
D0=$((${C[1]}  + ${C[2]}  + ${C[3]}  + ${C[4]}))
D1=$((${C[12]} + ${C[13]} + ${C[14]} + ${C[15]}))
D2=$((${C[23]} + ${C[24]} + ${C[25]} + ${C[26]}))
D3=$((${C[34]} + ${C[35]} + ${C[36]} + ${C[37]}))
# cpu usage per core
E0=$(echo "scale=1; (100 * ($B0 - $D0 - ${A[4]}   + ${C[4]})  / ($B0 - $D0))" | bc)
E1=$(echo "scale=1; (100 * ($B1 - $D1 - ${A[15]}  + ${C[15]}) / ($B1 - $D1))" | bc)
E2=$(echo "scale=1; (100 * ($B2 - $D2 - ${A[26]}  + ${C[26]}) / ($B2 - $D2))" | bc)
E3=$(echo "scale=1; (100 * ($B3 - $D3 - ${A[37]}  + ${C[37]}) / ($B3 - $D3))" | bc)
echo $E0
echo $E1
echo $E2
echo $E3

The average cpu usage per core can be directly computed from /proc/stat (Credits to @mikeserv for the hint for using /proc/stat.):

# Here we make use of bash direct array assignment
A0=($(sed '2q;d' /proc/stat))
A1=($(sed '3q;d' /proc/stat))
A2=($(sed '4q;d' /proc/stat))
A3=($(sed '5q;d' /proc/stat))
# user         + nice     + system   + idle
B0=$((${A0[1]} + ${A0[2]} + ${A0[3]} + ${A0[4]}))
B1=$((${A1[1]} + ${A1[2]} + ${A1[3]} + ${A1[4]}))
B2=$((${A2[1]} + ${A2[2]} + ${A2[3]} + ${A2[4]}))
B3=$((${A3[1]} + ${A3[2]} + ${A3[3]} + ${A3[4]}))
sleep 0.2
C0=($(sed '2q;d' /proc/stat))
C1=($(sed '3q;d' /proc/stat))
C2=($(sed '4q;d' /proc/stat))
C3=($(sed '5q;d' /proc/stat))
# user         + nice     + system   + idle
D0=$((${C0[1]} + ${C0[2]} + ${C0[3]} + ${C0[4]}))
D1=$((${C1[1]} + ${C1[2]} + ${C1[3]} + ${C1[4]}))
D2=$((${C2[1]} + ${C2[2]} + ${C2[3]} + ${C2[4]}))
D3=$((${C3[1]} + ${C3[2]} + ${C3[3]} + ${C3[4]}))
# cpu usage per core
E0=$(((100 * (B0 - D0 - ${A0[4]} + ${C0[4]})) / (B0 - D0)))
E1=$(((100 * (B1 - D1 - ${A1[4]} + ${C1[4]})) / (B1 - D1)))
E2=$(((100 * (B2 - D2 - ${A2[4]} + ${C2[4]})) / (B2 - D2)))
E3=$(((100 * (B3 - D3 - ${A3[4]} + ${C3[4]})) / (B3 - D3)))
echo $E0
echo $E1
echo $E2
echo $E3

or even shorter by making extensive use of bash direct array assignment:

# Here we make use of bash direct array assignment by assigning line
# 2 to 4 to one array


A=($(sed -n '2,5p' /proc/stat))
# user         + nice     + system   + idle
B0=$((${A[1]}  + ${A[2]}  + ${A[3]}  + ${A[4]}))
B1=$((${A[12]} + ${A[13]} + ${A[14]} + ${A[15]}))
B2=$((${A[23]} + ${A[24]} + ${A[25]} + ${A[26]}))
B3=$((${A[34]} + ${A[35]} + ${A[36]} + ${A[37]}))
sleep 0.2
# user         + nice     + system   + idle
C=($(sed -n '2,5p' /proc/stat))
D0=$((${C[1]}  + ${C[2]}  + ${C[3]}  + ${C[4]}))
D1=$((${C[12]} + ${C[13]} + ${C[14]} + ${C[15]}))
D2=$((${C[23]} + ${C[24]} + ${C[25]} + ${C[26]}))
D3=$((${C[34]} + ${C[35]} + ${C[36]} + ${C[37]}))
# cpu usage per core
E0=$((100 * (B0 - D0 - ${A[4]}  + ${C[4]})  / (B0 - D0)))
E1=$((100 * (B1 - D1 - ${A[15]} + ${C[15]}) / (B1 - D1)))
E2=$((100 * (B2 - D2 - ${A[26]} + ${C[26]}) / (B2 - D2)))
E3=$((100 * (B3 - D3 - ${A[37]} + ${C[37]}) / (B3 - D3)))
echo $E0
echo $E1
echo $E2
echo $E3

A top based solution

This can also be achieved without installing an additional tool with top only (I used this in a later post.) By default top does only show the average cpu load when it is started but it will show all cpus when you press 1. To be able to use top's cpu output when we use it in batch output mode we will need to make this the default behaviour when top is started. This can be done by using a ~/.toprc file. Fortunately this can be automatically created: Start top press 1 and press W which will generate the ~/.toprc file in your homefolder. When you now run top -bn 1 | grep -F '%Cpu' you will see that top now outputs all of your cores. Now we have already everything we will need to make this work. All the information I need is in column 3 of the array that will be the output of top.

There is only one problem: When the cpu usage for a core reaches 100% the array that the command outputs will move the column with the current load from column 3 to column 2. Hence, with awk '{print $3}' you will then see us, as output for column 3. If you're fine with that leave it. If not your could have awk print column 2 as well. It will just be :. A solution that avoids all those pitfalls is:

top -bn 2 | grep -F '%Cpu' | tail -n 4 | gawk '{print $2 $3}' | tr -s '\n\:\,[:alpha:]' ' '

it strips the output of all newlines \n, , and letters [:alpha:] and removes all but one single whitespace -s.

0

I came up with this solution and it works for me.

echo print `top -n 1 | tr -s " " | cut -d$" " -f10 | tail -n +8 | head -n -1 | paste -sd+ | bc`/ `nproc` | python

Source (write up): https://mohammadg.com/programming/how-to-get-overall-cpu-utilization-from-the-bash-command-line/

-1

you can see the frequency for each core with the following:

$ cat /proc/cpuinfo
1
  • The OP asked for CPU load per core expressed as a percentage, not the clock frequency. May 4, 2019 at 22:59

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.