1. Overview
In this tutorial, we’ll learn how to calculate the total CPU usage of a process. We’ll do this by writing a Bash script to perform arithmetic operations using the values from /proc/
First, we’ll see the content of the special file /proc/
Finally, we’ll write a Bash script to calculate the total CPU usage of a process.
2. Understanding the Values in /proc//stat
There is a special filesystem called proc in Linux systems that we usually mount on the /proc directory. This directory contains files with information about the system.
2.1. The /proc//stat File
Inside the /proc directory, each process has its own /proc/
The /proc/
Let’s use cat to see the status of the current shell. We can use the $$ special variable that is replaced with the current shell PID:
$ cat /proc/$$/stat
6608 (bash) S 2782 6608 6608 34818 25989 4194304 6423 130989 0 428 3 1 903 124 20 0 1 0 84265 9269248 1452 18446744073709551615 4345856 5123037 140728509762160 0 0 0 65536 3686404 1266761467 0 0 0 17 6 0 0 0 0 0 5362544 5410152 9601024 140728509767363 140728509767374 140728509767374 140728509771758 0
We can see several values separated by whitespace, and each value has its meaning.
For instance, the first value is the process ID, the second is the process’s name, and the third is the process’s state. We can see the full list of values and their meanings by running the man 5 proc command.
2.2. The CPU Usage
Inside the /proc/
Usually, we represent the CPU usage as a percentage of the elapsed time since the process started. So, the total CPU usage of an application is the sum of utime and stime, divided by the elapsed time.
To get the elapsed time since the process started, we can use the process’s start time. This value is the 22nd column from the /proc/
We can get the system’s uptime by reading the /proc/uptime file. The first value is the system’s uptime in seconds. Let’s see the content of /proc/uptime:
$ cat /proc/uptime
28057.65 218517.53
Now, we know how to calculate the total CPU usage. However, the process’s utime, stime, and starttime are not in seconds. Instead, they are in units called clock ticks. As the system’s uptime is in seconds, we have to convert the clock ticks to seconds to calculate the process’s CPU usage.
2.3. Converting From Clock Ticks to Seconds
We can get the number of clock ticks in a second by reading the system’s configuration. To do this, we can use the getconf command with the CLK_TCK parameter. Let’s get the CLK_TCK value:
$ getconf CLK_TCK
100
We can see that on our system, there are 100 clock ticks in a second. This value can be different, depending on the system configuration.
So, for instance, if the sum of a process’s utime and stime is 6000, and the CLK_TCK value is 100, then the process has used 60 seconds of CPU. Also, if the process’s starttime is 360000 clock ticks, then the process has started 3600 seconds (one hour) after the system booted.
3. Calculating the CPU Usage of a Process
Now that we’ve learned where we can find the values needed to calculate the CPU usage of a process, we can write a Bash script to calculate it.
3.1. Writing a Bash Script
We can split the CPU usage calculation into three steps. First, we get the process’s utime, stime, and starttime, and the system’s uptime. Then, we convert the process’s values from clock ticks to seconds. Finally, we calculate the elapsed time since the process started and divide the sum of utime and stime by the elapsed time. This way, we can easily calculate the total CPU usage of a process.
Let’s write a Bash script called total_cpu_usage.sh to perform the calculation:
#!/bin/bash
PID=$1
if [ -z "$PID" ]; then
echo Usage: $0 PID
exit 1
fi
PROCESS_STAT=($(sed -E 's/\([^)]+\)/X/' "/proc/$PID/stat"))
PROCESS_UTIME=${PROCESS_STAT[13]}
PROCESS_STIME=${PROCESS_STAT[14]}
PROCESS_STARTTIME=${PROCESS_STAT[21]}
SYSTEM_UPTIME_SEC=$(tr . ' ' </proc/uptime | awk '{print $1}')
CLK_TCK=$(getconf CLK_TCK)
let PROCESS_UTIME_SEC="$PROCESS_UTIME / $CLK_TCK"
let PROCESS_STIME_SEC="$PROCESS_STIME / $CLK_TCK"
let PROCESS_STARTTIME_SEC="$PROCESS_STARTTIME / $CLK_TCK"
let PROCESS_ELAPSED_SEC="$SYSTEM_UPTIME_SEC - $PROCESS_STARTTIME_SEC"
let PROCESS_USAGE_SEC="$PROCESS_UTIME_SEC + $PROCESS_STIME_SEC"
let PROCESS_USAGE="$PROCESS_USAGE_SEC * 100 / $PROCESS_ELAPSED_SEC"
echo The PID $PID has spent ${PROCESS_UTIME_SEC}s in user mode, ${PROCESS_STIME_SEC}s in kernel mode. Total CPU usage is ${PROCESS_USAGE_SEC}s
echo The process has been running for ${PROCESS_ELAPSED_SEC}s. So, the process has used ${PROCESS_USAGE}% of CPU
Now, we can test the script. Let’s find a process’s ID and execute the script by running ./total_cpu_usage.sh
$ ./total_cpu_usage.sh 4396
The PID 4396 has spent 416s in user mode, 126s in kernel mode. Total CPU usage is 542s
The process has been running for 8893s. So, the process has used 6% of CPU
As we can see, the script displayed all the information for the process ID 4396.
First, the script prints how much CPU time in seconds the process has used. And then, the script prints the CPU usage as a percentage of the elapsed time since the process started.
3.2. Understanding the Bash Script
Now, let’s review some parts of the previous Bash script.
First of all, we check if the script receives the process’s ID as a parameter. Then, we read the process’s status from the /proc/$PID/stat file and store it into an array.
We use the sed command to substitute anything in between parenthesis with an X character. We do this because the process’s name is placed inside parentheses and it can have whitespaces. If the process’s name has whitespaces and we don’t account for that situation, it would change the meaning of each column in the /proc/$PID/stat file.
After that, we get the process’s utime, stime, and starttime from the PROCESS_STAT array and store them into individual variables. We can notice we use the index 13, 14, and 21 instead of 14, 15, and 22. This is because Bash has zero-based arrays. So, the 14th column in the /proc/$PID/stat file becomes the 13th index in the array.
We also get the system’s uptime using the awk command after substituting the dot with a white space using the tr command. We do this because Bash’s arithmetic only works with integers.
Finally, we’ll also notice that we used let to perform the arithmetic operations.
4. Conclusion
In this article, we saw how to calculate the total CPU usage of an application.
First, we learned about /proc/
Finally, we wrote a script to calculate the total CPU usage of a process.