How to Check CPU Usage in Linux

By 

•

Updated on

•

11 min read

Checking CPU usage in Linux with terminal monitoring tools

A Linux server tells you what its CPUs are doing through several command-line tools, each with a different angle. top and htop show the live picture. mpstat and sar from the sysstat package report numeric samples that suit scripts and dashboards. vmstat from procps combines CPU, memory, and I/O statistics. ps attributes usage to specific processes, and uptime summarizes the load over the last fifteen minutes.

This guide explains how to read CPU usage from each of those tools, which one to reach for in which situation, and how to interpret common output fields.

Quick Reference

ToolBest forCommand
topLive snapshot, sort by CPUtop
htopInteractive, per-core viewhtop
mpstatPer-core sampling for scriptsmpstat -P ALL 1 5
vmstatSystem-wide CPU and memoryvmstat -y 1 5
sarCurrent or saved CPU samplessar -u 1 5
psPer-process attributionps -eo pid,user,%cpu,comm --sort=-%cpu | head
pidstatPer-process usage over an intervalpidstat -u 1 5
uptimeLoad average summaryuptime
nprocLogical core countnproc
lscpuCPU topology detailslscpu

Method 1: top

The top command is available on most Linux systems without any extra packages. Run it without arguments to open the live view:

Terminal
top

The header summarizes the CPU lines:

output
%Cpu(s):  4.2 us,  1.3 sy,  0.0 ni, 93.8 id,  0.5 wa,  0.0 hi,  0.2 si,  0.0 st

The fields tell you where CPU time goes. us is user-space code, sy is kernel code, id is idle, wa is time waiting for I/O, and st is time stolen by the hypervisor on a virtual machine. A high wa value points at slow disks rather than a CPU bottleneck.

Press 1 while top is open to switch from the aggregate line to one row per logical CPU. Press P to sort processes by CPU usage, M to sort by memory, and q to quit.

Method 2: htop

htop is an interactive replacement for top. It is available in the default repositories of most Linux distributions, but it may not be installed by default.

On Ubuntu, Debian, and Derivatives:

Terminal
sudo apt install htop

On Fedora, RHEL, and Derivatives:

On RHEL and its derivatives, enable the EPEL repository first. Then run:

Terminal
sudo dnf install htop

Run it the same way:

Terminal
htop

htop shows a colored bar per CPU at the top, a memory and swap bar, and a sortable process table below. Click a column header or use the function keys to sort. Search for a process with F3, filter with F4, and send a signal to a selected process with F9.

If you prefer a more graphical dashboard with CPU history graphs, see the guide on btop .

Method 3: mpstat

mpstat is part of the sysstat package and is the right tool when you need numeric CPU samples per core. Install it once before using mpstat or sar.

On Ubuntu, Debian, and Derivatives:

Terminal
sudo apt install sysstat

On Fedora, RHEL, and Derivatives:

Terminal
sudo dnf install sysstat

Sample all cores at one-second intervals five times:

Terminal
mpstat -P ALL 1 5
output
03:14:00 PM  CPU    %usr   %nice    %sys %iowait    %irq   %soft  %steal  %guest  %gnice   %idle
03:14:01 PM  all    3.27    0.00    0.75    0.00    0.00    0.00    0.00    0.00    0.00   95.98
03:14:01 PM    0    4.95    0.00    0.99    0.00    0.00    0.00    0.00    0.00    0.00   94.06
03:14:01 PM    1    1.00    0.00    1.00    0.00    0.00    0.00    0.00    0.00    0.00   98.00

The all row is the aggregate. Each numbered row is a single logical CPU. Use this output when one core is pinned at 100 percent and the others are idle, which can happen when an application is single-threaded.

Method 4: vmstat

The vmstat command is part of procps and combines CPU usage with memory, swap, and I/O. The first report normally shows averages since boot, so use -y when you want only interval samples. The columns at the right are the CPU summary:

Terminal
vmstat -y 1 5
output
procs -----------memory---------- ---swap-- -----io---- -system-- -------cpu-------
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st gu
 1  0      0 2708020  33996 1176632    0    0     0     0 1135   87 25  0 75  0  0  0
 1  0      0 2708020  33996 1176632    0    0     0     0 1146  144 25  0 75  0  0  0

r is the run queue length, which is how many processes are ready to use a CPU. A run queue consistently larger than the number of CPUs is a sign of CPU pressure. The cpu columns mirror the meaning of the top header. Newer procps releases also add a gu column. It shows the time spent running guest virtual machines.

In the output above, one process keeps the run queue at 1 and us sits at 25. On a four-core machine, that is one core running at full speed.

Method 5: sar

sar, also from sysstat, can print current samples and read saved activity logs. On Ubuntu 24.10 and newer, history collection is enabled when you install sysstat. On Debian and older Ubuntu releases, enable it manually:

Terminal
sudo sed -i 's/^ENABLED="false"/ENABLED="true"/' /etc/default/sysstat
sudo systemctl enable --now sysstat

The collector setup differs between distributions. If sar shows no saved history, check your distribution’s sysstat service or timer configuration.

Show current CPU samples:

Terminal
sar -u 1 5
output
03:15:01 PM     CPU     %user     %nice   %system   %iowait    %steal     %idle
03:15:02 PM     all      2.34      0.00      0.71      0.05      0.00     96.90
03:15:03 PM     all      2.18      0.00      0.69      0.04      0.00     97.09

Each row is a sample interval. Combine with -f to read an older log, for example sar -u -f /var/log/sysstat/sa10 for the tenth of the month. Ubuntu and Debian store the logs in /var/log/sysstat/. Fedora and RHEL use /var/log/sa/. This is the right tool when you need to confirm that a slowdown reported yesterday lined up with a CPU spike.

Method 6: ps

The ps command lists every process and lets you sort by CPU percentage. The pattern most admins reach for is:

Terminal
ps -eo pid,user,%cpu,%mem,comm --sort=-%cpu | head
output
    PID USER     %CPU %MEM COMMAND
   1437 dejan    23.5  4.2 firefox
   2049 dejan    11.2  2.8 node
   1102 root      4.7  0.9 systemd-journal

The output is a snapshot at the moment ps ran. The %CPU value is the average CPU usage over the lifetime of the process, not an instantaneous reading. For a live view, repeat the command with watch:

Terminal
watch -n 2 'ps -eo pid,user,%cpu,%mem,comm --sort=-%cpu | head'

watch reruns the command every two seconds. You get a refreshing list of the nine busiest processes without leaving the shell.

To measure per-process usage over an interval, use pidstat from the sysstat package. The following command samples every second, five times:

Terminal
pidstat -u 1 5

When the sampling ends, pidstat prints an average for each process that used CPU time:

output
Average:      UID       PID    %usr %system  %guest   %wait    %CPU   CPU  Command
Average:     1000      2978  100.00    0.00    0.00    0.00  100.00     -  sh
Average:     1000      2980    0.00    0.33    0.00    0.00    0.33     -  pidstat

The sh process used a full core for the whole interval. Unlike the ps value, this number reflects only the last five seconds.

Method 7: uptime and Load Averages

The uptime command is the smallest of the lot. Without arguments it prints the system uptime and the load average:

Terminal
uptime
output
 15:04:23 up  3:17,  2 users,  load average: 0.42, 0.35, 0.31

The three numbers are the average number of processes that are runnable or in uninterruptible sleep over the last 1, 5, and 15 minutes. Compare them with nproc:

Terminal
nproc
output
8

A load average consistently above the logical CPU count can indicate CPU contention, but it can also reflect processes blocked on I/O. A lower load does not rule out a bottleneck, such as one saturated CPU or a slow disk. Check the per-CPU usage and I/O wait alongside the load average. The 15-minute value is the trend; the 1-minute value reacts to short spikes. For a closer look at how to read these numbers, see the guide on load average in Linux .

Method 8: CPU Usage in a Script

Sometimes you need a single number, for example in a monitoring script or an alert. Take one mpstat sample and subtract both idle time and I/O wait from 100:

Terminal
LC_ALL=C mpstat 1 1 | awk '$1 == "Average:" && $2 == "all" {print 100 - $6 - $NF}'
output
25.19

mpstat waits one second and prints an Average line. LC_ALL=C keeps the label in English and uses a decimal point regardless of your locale. awk selects the aggregate row and subtracts %iowait in the sixth field and %idle in the last field from 100.

If sysstat is not installed, read the counters from /proc/stat directly. The first line of that file holds the time each CPU state has used since boot. Both examples exclude idle time and I/O wait but include steal time on virtual machines. The script below reads the counters twice, one second apart, and calculates the share of the difference:

~/cpu-usage.shsh
#!/bin/bash

read -r cpu user nice system idle iowait irq softirq steal _ < /proc/stat
total1=$((user + nice + system + idle + iowait + irq + softirq + steal))
idle1=$((idle + iowait))

sleep 1

read -r cpu user nice system idle iowait irq softirq steal _ < /proc/stat
total2=$((user + nice + system + idle + iowait + irq + softirq + steal))
idle2=$((idle + iowait))

echo "CPU usage: $(( 100 * ((total2 - total1) - (idle2 - idle1)) / (total2 - total1) ))%"

Run the script with bash:

Terminal
bash ~/cpu-usage.sh
output
CPU usage: 25%

Bash arithmetic works with integers only, so the result is rounded down. A single reading from top in batch mode is less reliable for this job. The first frame it prints is not a fresh one-second sample.

Method 9: Capacity Context

Before you read CPU usage, know what the host has. lscpu summarizes the CPU model, sockets, cores, and threads. For a deeper hardware view, see the guide on checking CPU information in Linux .

Terminal
lscpu
output
Architecture:            x86_64
CPU op-mode(s):          32-bit, 64-bit
Address sizes:           46 bits physical, 48 bits virtual
Byte Order:              Little Endian
CPU(s):                  8
On-line CPU(s) list:     0-7
Vendor ID:               GenuineIntel
Model name:              Intel(R) Core(TM) i7-9700K CPU @ 3.60GHz
Thread(s) per core:      1
Core(s) per socket:      8
Socket(s):               1

Thread(s) per core: 1 means hyper-threading is disabled or not present, so each logical CPU is a physical core. A high CPU usage on a chip with eight physical cores is different from the same number on a four-core chip with hyper-threading.

Troubleshooting

mpstat: command not found
The sysstat package is not installed. Install it with sudo apt install sysstat on Ubuntu, Debian, and Derivatives, or sudo dnf install sysstat on Fedora, RHEL, and Derivatives.

sar reports Cannot open /var/log/sysstat/
Sample collection is not enabled. Set ENABLED="true" in /etc/default/sysstat and enable the service with sudo systemctl enable --now sysstat. Wait at least one collection interval (10 minutes by default) before rerunning sar.

top shows more than 100% CPU for one process
The default mode shows percentage relative to a single CPU, so a multi-threaded process can exceed 100 percent. Press Shift+I to toggle Irix mode off, and the value normalizes across all CPUs.

Load average looks high but no process consumes CPU
Load includes processes in D state, which is uninterruptible sleep. Check ps -eo state,pid,comm | awk '$1 ~ /D/' to find processes blocked on I/O. The cause is usually a slow or stalled disk.

CPU bars are missing in htop
The CPU meters were removed from the htop configuration. Press F2, open Meters, and add the CPUs (1/1) meter to one of the columns. htop saves the change to ~/.config/htop/htoprc.

FAQ

Which tool should I check first?
Run top or htop for a live picture, then drop to mpstat and sar when you need numbers or history. ps answers the per-process question once you know there is a problem.

What does %steal mean?
%steal is the time the hypervisor took from the guest VM to run something else. A consistent non-zero %steal value on a VM means the host is over-committed and the guest cannot get the CPU it asked for.

How do I count CPUs from a script?
Use nproc for the logical CPU count, or nproc --all to include offline CPUs. Both return a single integer and are stable across distributions.

Is high CPU always bad?
No. A batch job can use all available CPU capacity without causing a problem. High usage needs attention when it causes slow responses, missed deadlines, or sustained CPU queueing. Load average alone cannot tell you whether the CPU is the bottleneck because it also includes processes blocked on I/O.

Conclusion

Start with top or htop to find a busy process, then use pidstat to measure its CPU usage over an interval. If the slowdown has already passed, check the saved sar samples for that period.

Linuxize Weekly Newsletter

A quick weekly roundup of new tutorials, news, and tips.

About the authors

Dejan Panovski

Dejan Panovski

Dejan Panovski is the founder of Linuxize, an RHCSA-certified Linux system administrator and DevOps engineer based in Skopje, Macedonia. Author of 1000+ Linux tutorials with 20+ years of experience turning complex Linux tasks into clear, reliable guides.

View author page