Calculating CPU Usage from /proc/stat

I recently came across a number of forum topics asking how to calculate CPU usage from /proc/stat, which raised a very slight sense of challenge within me (was just in "the mood" to do some BASH scripting)... so, here's an example showing how to calculate CPU usage with a simple BASH script.

#!/bin/bash
# by Paul Colby (http://colby.id.au), no rights reserved ;)

PREV_TOTAL=0
PREV_IDLE=0

while true; do
  CPU=(`cat /proc/stat | grep '^cpu '`) # Get the total CPU statistics.
  unset CPU[0]                          # Discard the "cpu" prefix.
  IDLE=${CPU[4]}                        # Get the idle CPU time.

  # Calculate the total CPU time.
  TOTAL=0
  for VALUE in "${CPU[@]}"; do
    let "TOTAL=$TOTAL+$VALUE"
  done

  # Calculate the CPU usage since we last checked.
  let "DIFF_IDLE=$IDLE-$PREV_IDLE"
  let "DIFF_TOTAL=$TOTAL-$PREV_TOTAL"
  let "DIFF_USAGE=(1000*($DIFF_TOTAL-$DIFF_IDLE)/$DIFF_TOTAL+5)/10"
  echo -en "\rCPU: $DIFF_USAGE%  \b\b"

  # Remember the total and idle CPU times for the next check.
  PREV_TOTAL="$TOTAL"
  PREV_IDLE="$IDLE"

  # Wait before checking again.
  sleep 1
done

The script should be pretty self-explanatory. It loops forever (just hit Ctrl-C when you want it to stop). Each time through the loop, it calculates the CPU usage by subtracting the CPU's "idle" time from the CPU's "total" time. Notice the way the percentage is calculated by multiplying by 1000, and then dividing by 10 at the end, instead of the "normal" approach of multiplying by 100. This is actually a cunning way of rounding off :) See the way I add 5 just before the divide by 10? So, for example, if the percentage was 45.6% then BASH would truncate the result to 45%. But, by multiplying by 1000 (instead of 100), and adding 5, the result is 461 (ie 456+5), which, when divided by 10 at the end, yields the correctly rounded result of 46% :) The other neat trick in the above script is the output "echo" statement:

echo -en "\rCPU: $DIFF_USAGE%  \b\b"

This causes the script to output the CPU % on just one line - ie continually overwriting the previous value. It works like this:

  • the "-e" parameter causes the "echo" command to interpret the "\r" and "\b" escape codes;
  • the "-n" parameter prevents the "echo" command from appending the usual carriage-return to the end of the line;
  • the two spaces after the "%" sign are there to overwrite previous characters that would be left over when going from double (or triple) digits down to single (or double) digits;
  • and finally, the two "\b" (backspace) codes put the cursor back to being immediately after the "%" symbol, otherwise the the two spaces above kinda leave the cursor hanging in the middle of nowhere.

Well, that's it. Any questions, or suggestions, add a comment below :)

Trackback URL for this post:

http://colby.id.au/trackback/39

/proc/stat mit Bash auswerten

Ich wollte die aktuelle CPU Auslastung per Bashscript ermitteln, so wie sie auch unter top angezeigt wird. Nach einigem suchen wurde ich bei einem Script fündig, welches sehr anschaulich verdeutlichte wie dies realisiert werden kann. Auf dessen Basis h...

CPU check plugin for Nagios

I’ve just finished version 1.0 of a CPU check plugin which is sh-compliant (as always) and calculates utilization through Jiffies rather than using another third party tool like iostat or top. This means it should work on all Linux machines right...

Nice

That was a good little read. (Somewhat useful too!)

I was wondering about the rounding and was going to ask; why don't you use higher percision (like 45.6%)?

Higher precision

Ok, so the next version of the script (out soon) will indeed include single-decimal precision, just like your example of 45.6% :)

But for anyone who can't wait for the next version, the added decimal is very easy to do... just remove these two lines:

  let "DIFF_USAGE=(1000*($DIFF_TOTAL-$DIFF_IDLE)/$DIFF_TOTAL+5)/10"
  echo -en "\rCPU: $DIFF_USAGE%  \b\b"

and replace them with:

  let "DIFF_USAGE=1000*($DIFF_TOTAL-$DIFF_IDLE)/$DIFF_TOTAL"
  let "DIFF_USAGE_UNITS=$DIFF_USAGE/10"
  let "DIFF_USAGE_DECIMAL=$DIFF_USAGE%10"
  echo -en "\rCPU: $DIFF_USAGE_UNITS.$DIFF_USAGE_DECIMAL%    \b\b\b\b"

Of course, the same approach can be extended to multiple decimal places, but anything more than a single decimal place would be implying a false sense of accuracy, I would think.

Anyway stay tuned for the official 1.1 version soon... ;)

Interesting...

I just saw that my example script above has been extended in this forum post... nice to see it helping someone - even if only for discussion ;)

That's pretty cool, too bad

That's pretty cool, too bad he didn't link back to you. :-(

Oh well... Still nice to see someone utilizing it... and your name is still in there!!!

Another copy...

Just noticed another copy of my script here: http://hramn.icedslash.com/no~star/cpuchck

it's nice to see it gettin' around :)

good post.

Thank you much for that good piece of text.

nice but can you help...

Basically, I'd like to adapt your code to add to a script I'm writing that simply logs the cpu temp of my system along with motherboard temp and a few other things I get from sensors.

Here's the thing, I just need a static cpu usage to be logged to my file. In other words, the script runs, it grabs:

1) cpu temp
2) mb temp
3) cpu %

It then waits 15 sec or so and repeats. Each time it logs the data to a text file. Simple. I just need some help adapting your code to output the cpu% when the script runs. Also, can you make the output to the nearest 1%. Right now it's rounds to the nearest 5 %.

Here is an example of my script grabbing the cpu temp:

c0=`sensors | grep -A 1 'Core 0' | cut -c15-16 | sed '/^$/d'`

So basically, I need a variable for cpu% based on your code.

Thank you!

Should be possible

It should not be too hard to do what you have in mind. Basically, you'd need to do two things to the above script for it work:

1. strip the "while" loop out completely.

2. save/restore PREV_TOTAL and PREV_IDLE via temporary files.

Since the script depends on knowing the previous total and idle times (the only way to calculate the "differences" needed to find the "current" CPU load), those times will need to be stored somewhere.

So, at the start of the script, check for the existence of the temp files eg /tmp/prevCpuIdle.txt and /tmp/prevCpuTotal.txt, and initialise PREV_TOTAL and PREV_IDLE to the contents of those files accordingly, or 0 if they don't exist yet.

Then, at the end of the script, simply write out the $TOTAL and $IDLE times to those two files.

Of course, there's many other things to consider if you want it to be robust (eg how often does the machine wipe /tmp, and will this script *ever* run multiple instances simultaneously). But that should get you going :)

PS - I'd probably write the temp files to /shm or /dev/shm if you have one - especially if you will be running this script often! ;)

paul.

good

can i get this logic in C.... am a new bee

WONDERFULL!

This is Just what I needed! I modified it so I could use it in my $PS1. ($ PS1="\`CPU_Usage.sh\` $PS1")
Very useful when using SSH. Works great! Thanks!

(o_
//\       BE AWARE OF THE PENGUIN!!!
V_/_

very clever!

I would never have thought to use it in a $PS1... that's a brilliant idea!! I'll definitely give that ago sometime soon :)

just great

This is just great Paul thanks! Now, I've been trying to do the same but for a specific process, currently taking info from /proc/PID/stat but things there are not as clear as /proc/stat , mind to share some thoughts?

Looks doable

Looks doable. You'd want to get the utime and stime (and maybe cutime and cstime, if you want to include children). But they're in clock ticks, which you'd need to compare to the totals from /proc/stat, which are in USER_HZ... would take a little bit of investigating to figure out a reliable cross-platform way to compare clock ticks with USER_HZ (it's probably really easy, but I've just never thought about it).

Hmm... will have to look deeper another time - you've got my curiosity up now ;)

pc.

i like

i like it however i would like to only put out a single number as a simple snapshot of what the cpu is doing to redirect it to a file.

Coming soon...

Yeah, I'm working on a version that supports either mode :)

But in the meantime, check out my comment above for a quick explanation of how you can do it yourself if you like ;)

pc.

I am gonna adopt your code..

I just want to read proc stat file twice .

So I am gonna get rid of while loop. Write a function to read file and initizlize previous and new variables.

This function will be called two times with a sleep for 1 second between them, so that I get realtime CPU utilizn for duration of 1 second.

This bash script will be plinked from remote machine for monotoring.

Great stuff here, keep up the

Great stuff here, keep up the good work!

Hi Paul, I have built a

Hi Paul,

I have built a Node.js service for remote CPU usage streaming and event notifications. Some of the concepts are based on your script here. Thank You :)

The url is:
https://github.com/last/healthjs

-brendon

Health.js for Node.js

Hi Brendon, that's looks pretty cool! :)

Thanks for letting me know, and for including a reference to this blog post in the README file!! :)

pc.

health.js looks pretty sweet!

Thanks for sharing that back on this blog. I might never have noticed it.
I'll load it up on one of my servers soon and see how it goes.

Great job.

-Mike

Hi, I used your script... Is

Hi,
I used your script...
Is it not compatible for all shell?
I got below error -
10: Syntax error: "(" unexpected (expecting "done")

Line 10 : CPU=(`cat /proc/stat | grep '^cpu '`)

Any Idea?

No sure...

I don't know much about other shells... what shell are you using?

The error message suggests to me that the error is somewhere *before* line 10... have you made any changes to the script above that line? (Since the quoted line is #8 in my script, I suppose you probably have).

Feel free to paste the script as you have it now, and I'll take a look :)

pc.

I got the same error when exec this script

I got the same error when exec this script,but if you use this command "/bin/bash script_name.sh",It will be ok.....
I think ,This script just not compatible with sh....

thank you

Hi,

i took your script and edited to feed the data in an rrdtool-database and generate some nice graphs with it.
its awesome, thank you

Greetz, DerMicha

You're welcome

You're very welcome! Thanks for letting me know :)

pc.

This function will be called

This function will be called two times with a sleep for 1 second between them, so that I get realtime CPU utilizn for duration of 1 second.

Padded output

To make the output percentage padded with spaces (i.e. the output will always have the same length), i use:

printf "\r%s%3d%%" "CPU:" "$DIFF_USAGE"

instead of

echo -en "\rCPU: $DIFF_USAGE% \b\b"

Modification for multiple cpu's

This won't work as expected if your machine has multiple cpu's, since multiple lines will match ^cpu.

Instead you should do something like CPU=(`cat /proc/stat | head -n 1`)

Good point

Thanks, you're quite right! :)

I guess, depending on the desired result, one could either use something like 'head' as you suggested to select just the combined-CPUs line (perhaps with an optional script argument to specify which CPU to monitor instead), or track each CPU individually and print them all as individual percentages.

I might implement one, or both sometime... :)

Thanks again!

paul.

Hi Paul. I'm using your

Hi Paul. I'm using your script to try and get CPU utilization on my dual-core Android phone. I've added code to track CPU0 and CPU1 independently (that is what I really need) but it gets a bit tricky. The way OMAP4 works is that CPU1 shuts off at times resulting in CPU1 data not even showing up in /proc/stat output. The result is that the script balks like this:

CPU0: 9%
./cpu_usage.sh: line 48: let: DIFF_IDLE1=-: syntax error: operand expected (error token is "-")
./cpu_usage.sh: line 50: let: DIFF_USAGE1=(1000*(0--1200050)/0+5)/10: division by 0 (error token is "+5)/10")
CPU1: 2%

There are ways to fix this and just force output of CPU1 to = 0 when the /proc/stat output doesn't have a CPU1 (or something like that), but for now it works good enough for me because I'm most interested to see when the 2nd core goes on/off, so I now just look for these error messages :-)

Anyway, thought you might like to know how someone else is using your script. Very useful, THANKS!

-Michael

Sounds pretty cool :)

Hi Michael, that sounds pretty cool! :)

Thanks for taking a minute to let me know :)

pc.

MUltiple cp

Hi Paul

I have seen your thread. I was wondering if you have got a script that will monitor multiple cp individually.

Wayne Mc Naughton

Just saw this...

Just saw my script got re-used for this: 3 Hour Hacks: Part 1.

Pretty sweet! :)

pc.

lol. awesome.

That hack is funny and clever.
I love the idea of a 3 hour hack, but I'm pretty sure my 3 hours would end up being spread out. I also think I'd end up spending 5 hours because that's how I roll. lol.

beatifull

thnaks for sharing.

MUltiple CPU

Hi Paul

HAve you got an updated script for multiple cpu's?

This is Just what I needed! I

This is Just what I needed! I modified it so I could use it in my $PS1.
Regards,
Tony

Awesome Script!!!

Hi Paul,

Many Thanks for sharing this wonderful script. I'm using it, though I'm modifying it to suite my requirement.
Again thanks for sharing :)

Thanks and Regards,
Prasanna

Great Job

Thank you for your script, it is very helpful.

Regards,
Ermal

Post new comment