16
\$\begingroup\$

I have been developing the below method to calculate the CPU usage of a Linux system at a point in time, as a percentage of the total number of cores available in the system.

#!/bin/bash
# Get number of cores from proc cpuinfo
CORECOUNT=`grep -c ^processor /proc/cpuinfo`
# Use top, skip the first 7 rows, count the sum of the values in column 9 - the CPU column, do some simple rounding at the end. 
CPUUSAGE=`top -bn 1 | awk 'NR>7{s+=9ドル} END {print s/'$CORECOUNT'}' | awk '{print int(1ドル+0.5)}'`
printf "$CPUUSAGE\n"

Would this be considered a reasonably accurate way of doing this? Any suggestions to increase efficiency/accuracy would be greatly appreciated.

rolfl
98.1k17 gold badges219 silver badges419 bronze badges
asked Sep 9, 2014 at 11:37
\$\endgroup\$
1
  • \$\begingroup\$ PS. good for you in figuring in the # cores...ala "Irix" cpu timekeeping... which helps know # of cores and multithreading. I.e. if a process only ever uses 100% cpu, it is likely 1 thread; if >100%, likely more than one thread. Doesn't equate 50%cpu on a single core machine with 50% on a 12 core (like many windows utils do). \$\endgroup\$ Commented Sep 9, 2014 at 19:04

2 Answers 2

8
\$\begingroup\$

There are a few problems you have here. The first, is the concept of getting the usage at a point in time, which is not possible. But, you can get it for a span of time. You are using top -bn 1 which will get the usage for the span of a second.

Since you are accessing the /proc system to get the number of CPU's, you may as well use the right /proc file for what you want:

cat /proc/stat:

 ~ $ cat /proc/stat
cpu 177206 161827 1274669 3582291272 144435 154779 0 0 0 0
cpu0 32170 48533 914773 894903107 30650 69784 0 0 0 0
cpu1 52790 41088 180295 895685563 76035 14410 0 0 0 0
cpu2 18745 40955 49422 895959636 11295 2234 0 0 0 0
cpu3 73500 31249 130178 895742965 26454 68350 0 0 0 0
intr 339571688 48 10 0 0 0 0 2 0 1 0 0 0 152 0 0 8750527 0 322394 40428015 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
......
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
ctxt 660707018
btime 1401123121
processes 196075
procs_running 1
procs_blocked 0
softirq 322202763 0 136092603 1792784 40737199 4701209 0 6 133008518 51951 5818493

With the above file, I would calculate it manually.... (also using the documentation here: http://www.mjmwired.net/kernel/Documentation/filesystems/proc.txt#1212 )

I have taken the liberty of programming it myself.... since the recommendation is sucha deviation from your solution, it makes sense:

#!/bin/bash
#CORECOUNT=$(grep cpu /proc/stat | grep -v 'cpu ' | wc -l)
#echo $CORECOUNT cores not needed for calculation though
DELAY=${1:-1}
function getstat() {
 grep 'cpu ' /proc/stat | sed -e 's/ */x/g' -e 's/^cpux//'
}
function extract() {
 echo 1ドル | cut -d 'x' -f 2ドル
}
function change() {
 local e=$(extract $ENDSTAT 1ドル)
 local b=$(extract $STARTSTAT 1ドル)
 local diff=$(( $e - $b ))
 echo $diff
}
#Record the start statistics
STARTSTAT=$(getstat)
sleep $DELAY
#Record the end statistics
ENDSTAT=$(getstat)
#http://www.mjmwired.net/kernel/Documentation/filesystems/proc.txt#1236
#echo "From $STARTSTAT"
#echo "TO $ENDSTAT"
# usr nice sys idle iowait irq guest
#From 177834 168085 1276260 3584351494 144468 154895 0 0 0 0
#TO 177834 168085 1276261 3584351895 144468 154895 0 0 0 0
USR=$(change 1)
SYS=$(change 3)
IDLE=$(change 4)
IOW=$(change 5)
#echo USR $USR SYS $SYS IDLE $IDLE IOW $IOW
ACTIVE=$(( $USR + $SYS + $IOW ))
TOTAL=$(($ACTIVE + $IDLE))
PCT=$(( $ACTIVE * 100 / $TOTAL ))
echo "BUSY $ACTIVE TOTAL $TOTAL $PCT %"
answered Sep 9, 2014 at 13:55
\$\endgroup\$
2
  • \$\begingroup\$ Nice! If you post that code for code review, maybe I can improve on it a bit ;-) \$\endgroup\$ Commented Sep 9, 2014 at 15:19
  • \$\begingroup\$ @janos - good idea, and done \$\endgroup\$ Commented Sep 9, 2014 at 15:33
5
\$\begingroup\$

Let's assume that column 9 in the output of the top command is a good measure of the CPU usage. I don't really know, but let's assume.

Your script could be written cleaner and better:

  1. Don't use `...` style process substitution, use $(...) instead
  2. Usually, there's no point piping the output of one awk command to another. You can do what you did in a single awk, which is better, because you save one unnecessary process
  3. Better to pass variables to awk using the -v parameter than trying to embed in the command line
  4. Don't use printf when an echo is enough and simpler
  5. Avoid extremely long lines. The mouse wheel scrolls up-down fine, left-right is a PITA. You could break that long comment to 2 lines.

Suggested implementation:

#!/bin/bash
# Get number of cores
CORECOUNT=$(grep -c ^processor /proc/cpuinfo)
# Use top, skip the first 7 rows, count the sum of the values
# in column 9 - the CPU column, do some simple rounding at the end
CPUUSAGE=$(top -bn 1 | awk -v n=$CORECOUNT 'NR > 7 { s += 9ドル } END { print int(s / n + .5); }')
echo $CPUUSAGE

Btw, do you really need $CPUUSAGE? If not, just omit the variable, change the last line:

top -bn 1 | awk -v n=$CORECOUNT 'NR > 7 { s += 9ドル } END { print int(s / n + .5); }'

(I used n as the Awk variable name mainly to make my answer fit on the page without scrolling ;-) Feel free to name it corecount or as you like.)

answered Sep 9, 2014 at 13:39
\$\endgroup\$
2
  • \$\begingroup\$ Re: comment #4 above -- echo is not portable. printf is the posix recommended way of printing for portability (primarily because of echo's behavior with expanding inline codes ("\n") w/r/t the -e/-E switches and how it works with suppressing newlines (-n), and whether or not -- suppresses further argument processing... I.e. those items aren't the same everywhere... so printf ended up getting the standards vote. (I probably more often use echo...) \$\endgroup\$ Commented Sep 9, 2014 at 18:57
  • \$\begingroup\$ I know. I wouldn't recommend echo for anything involving any of its switches, they are a complete mess. But in this particular case, using printf just for a simple printing with a newline, that's just tedious. \$\endgroup\$ Commented Sep 9, 2014 at 19:05

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.