patternbashMinor
Using /proc/stat to calculate CPU usage
Viewed 0 times
usagestatproccalculateusingcpu
Problem
Inspired by this question: To calculate the total CPU usage as a percentage
I wrote the following bash script (as an answer) to calculate the CPU usage for a (configurable) period over all CPU cores on a host.
The key features I wanted to ensure are just a single read of the stat file at the beginning, and another single read at the end.
There is some debate about what constitutes 'active' time (Is IOWait busy, or idle time?) If you have insights on that, I would appreciate that too.
All suggestions and criticisms welcome.
I wrote the following bash script (as an answer) to calculate the CPU usage for a (configurable) period over all CPU cores on a host.
#!/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 %"The key features I wanted to ensure are just a single read of the stat file at the beginning, and another single read at the end.
There is some debate about what constitutes 'active' time (Is IOWait busy, or idle time?) If you have insights on that, I would appreciate that too.
All suggestions and criticisms welcome.
Solution
Nice script :-)
In
In bash you can use here strings, which is better than using
But actually, if we keep the spaces as the separator in
getstat is a bit odd:- It runs a
grepand then ased, when probably a singlesed(orawk) could do the job
- It replaces spaces with
x, but not very clear why:
- I see that
extractusesxas the separator, but it could just as well use space for that
- I also see that using
xas the separator makes thechangefunction slightly simpler, because it lets you writeextract $ENDSTATinstead ofextract "$ENDSTAT", but then again, having to quote doesn't seem a big problem
In
extract, you run an echo and then a cut:echo $1 | cut -d 'x' -f $2In bash you can use here strings, which is better than using
echo:cut -d 'x' -f $2 <<< $1But actually, if we keep the spaces as the separator in
getstat, then we can rewrite both of these functions with fewer extra processes:function getstat() {
sed -ne '/^cpu */s/^cpu *//p' /proc/stat
# that is:
# -n to not print lines by default
# for lines matching /^cpu */, delete /^cpu */, and print the line
}
function extract() {
# example 1st param: "437797 1219 185341 549955584 58662 4 7083 0 0 0"
# example 2nd param: 1, 2, 3, ... (the n-th column to use)
n=$2 # saving the position param
set -- $1 # reset positional params from $1, so that $1=437797, $2=1219, ...
echo ${!n} # echo the n-th positional param
}
function change() {
local e=$(extract "$ENDSTAT" $1)
local b=$(extract "$STARTSTAT" $1)
echo $(( $e - $b ))
}Code Snippets
echo $1 | cut -d 'x' -f $2cut -d 'x' -f $2 <<< $1function getstat() {
sed -ne '/^cpu */s/^cpu *//p' /proc/stat
# that is:
# -n to not print lines by default
# for lines matching /^cpu */, delete /^cpu */, and print the line
}
function extract() {
# example 1st param: "437797 1219 185341 549955584 58662 4 7083 0 0 0"
# example 2nd param: 1, 2, 3, ... (the n-th column to use)
n=$2 # saving the position param
set -- $1 # reset positional params from $1, so that $1=437797, $2=1219, ...
echo ${!n} # echo the n-th positional param
}
function change() {
local e=$(extract "$ENDSTAT" $1)
local b=$(extract "$STARTSTAT" $1)
echo $(( $e - $b ))
}Context
StackExchange Code Review Q#62425, answer score: 6
Revisions (0)
No revisions yet.