There are several ways to accomplish sub-sccond polling of the cpu load, either by using a utility, like dstat, (example below) or by directly polling /proc/stat, (example also below).
Let’s go over the pros and cons for both, before going on to the technical examples.
To use dstat, you will need to run a fast crontab( */1 * * * * ) and pipe the result to a statistics file that you can check. The upside is that your SNMP timeouts aren’t going to be an issue, the downside, it’s not truly instantaneous, and running the crontab when you’re not actually looking for this data has an impact. The impact may be negligible, but still, it’s there.
To use /proc/stat, you must poll the content of /proc/stat twice. The contents of /proc/stat are cumulative from boot. So the first poll and second poll results need to be subtracted from each other and then the calculation for the current load can be made. The downside is that there must be some form of delay to make this calculation. In the example below, I’ve gotten the delay down to sub-second. This would meet your needs, however the data samples are so close together I’m not sure how absolute the accuracy is.
Using dstat;
Add this line to /etc/crontab:
*/1 * * * * root echo $((100-`dstat -c -C0 --noheaders --nocolor 1 1 | grep -v "\-\|u" | awk 'NR == 2' | tr -s " " | cut -d \ -f 4`)) > /tmp/cpuload
This only updates once every minute. If you want more frequent updates, add a second line and preface the command with sleep 30, like
*/1 * * * * root sleep 30; echo $((100-`dstat -c -C0 --noheaders --nocolor 1 1 | grep -v "\-\|u" | awk 'NR == 2' | tr -s " " | cut -d \ -f 4`)) > /tmp/cpuload
It is possible to use(abuse) cron even farther and get to sub second results, but that's another topic entirely.
explanation:
dstat -c -C 0 --noheaders --nocolor 1 0
-c only show cpu data
-C select cpu0. change numbering to select other cpu
--noheaders --nocolor (implied --noupdate) simplify what we see
1 one second delay in reading of stats
1 exit after second read of stats. Giving it time to settle down after invocation.
grep -v "-\|u"
remove non data lines
awk 'NR == 2'
select the second line.
tr -s " "
trim out the extra spaces that look nice on screen but not for system use
cut -d \ -f 4
-d \ (there is a space after the \ (escape) space delineated line
-f 4 select the idle. yes it's visually 3, but the space at the start of the line counts as a field, throwing off the field count.
$ (( ))
bash arithmetic operations, subtracting the system idle from 100.
Using /proc/stat;
Save as cpuload.sh;
#!/bin/bash
#Calculation delay. Without a delay, there is no way to determine current
#values. The content or /proc/stat is cumulitative from last boot.
# in seconds; sleep must be able to support float values
dly=3
function calculate {
#load arrays
IFS=' ' read -r -a firstarr <<< "$1"
IFS=' ' read -r -a secondarr <<< "$2"
#clear name fields in array so that calculations don't get messy
firstarr[0]=0 ;
secondarr[0]=0 ;
#clear values
firsttotcpu=0
secondtotcpu=0
#calculate the begining interrupt counts
for f in ${firstarr[@]};
do
let firsttotcpu+=$f;
done
firstidle=$((${firstarr[4]}+${firstarr[5]}));
#calculate the ending interrupt counts
for l in ${secondarr[@]};
do
let secondtotcpu+=$l;
done;
secondidle=$((${secondarr[4]}+${secondarr[5]}));
#calculate the relative change counts
insttotcpu=$(( secondtotcpu - firsttotcpu ))
instidle=$(( secondidle - firstidle ))
#calculate the utilization percentage. must be done external to bash as it's a
#floating calculation
cpu_load=$( echo | awk -v tot=$insttotcpu -v idl=$instidle ' { print ( ( ( tot - idl ) / tot ) * 100 ) } ' )
echo -n $cpu_load " "
}
export -f calculate
#main execution
oldIFS=$IFS
IFS=$'\n' cpu_start=( $( grep cpu /proc/stat ) );
#must delay to get difference
sleep $dly
IFS=$'\n' cpu_end=( $( grep cpu /proc/stat ) );
cpucount=${#cpu_start[@]}
#uncomment this for loop to enable printing the cpu name above the percentages
#for i in ${cpu_start[@]};
# do
# IFS=' ' read -r -a name <<< "$i"
# echo -n ${name[0]} " "
#done
#echo ""
for (( i=0; i<$cpucount; i++ ))
do
calculate "${cpu_start[$i]}" "${cpu_end[$i]}"
done
echo ""
IFS=$oldIFS