TechEarl

pidstat: Monitor Per-Process CPU, Memory, and I/O

The pidstat command samples per-process CPU, memory, and disk I/O at a fixed interval. pidstat -u 2, pidstat -r 2, pidstat -d 2, scoping to one PID with -p, threads with -t, and where it beats top.

Ishan Karunaratne⏱️ 9 min readUpdated
Share thisCopied
The pidstat command reports per-process CPU with pidstat -u, memory and page faults with -r, disk I/O with -d, scoped to a PID with -p, threads with -t, sampled every N seconds. From the sysstat package.

The pidstat command reports CPU, memory, and disk I/O usage broken down by individual process, sampled at a fixed interval. pidstat -u 2 prints per-process CPU every two seconds; pidstat -r 2 does the same for memory; pidstat -d 2 for disk I/O. It ships in the sysstat package alongside sar, iostat, and mpstat, and it is the tool I reach for when top can show me that the box is busy but not which process is causing it over time.

The reason pidstat beats eyeballing top is the interval sampling. Each line is an average over the sampling window, not a single instantaneous reading, so a process that spikes for 200ms every few seconds shows up cleanly instead of flickering past. Point it at one PID and let it run, and you get a per-second (or per-N-second) trace of exactly what that process is doing to the machine.

Install it: the sysstat package

pidstat is not always present on a minimal install. It comes from sysstat:

bash
# Debian / Ubuntu
sudo apt install sysstat

# RHEL / Rocky / Alma / Fedora
sudo dnf install sysstat

# Alpine
sudo apk add sysstat

Once installed, pidstat with no arguments prints one block: the CPU usage of every active task since system boot. That is rarely what you want. The interval is what makes the tool.

Per-process CPU: pidstat -u

bash
pidstat -u 2

-u reports CPU utilization. The 2 is the interval in seconds, so this samples every two seconds and prints a new block each time. Output looks like this:

code
07:14:32      UID       PID    %usr %system  %guest   %wait    %CPU   CPU  Command
07:14:34     1000      4821   38.50    2.00    0.00    0.50   40.50     3  node
07:14:34        0       912    0.50    1.50    0.00    0.00    2.00     1  containerd

%usr is time in user space, %system is time in the kernel on this process's behalf, %CPU is the total. The CPU column is which logical core the process was last seen on. %wait is time the task spent runnable but waiting for a core, which is your signal that the box is CPU-saturated rather than the process being slow on its own.

To bound the run, add a count after the interval:

bash
pidstat -u 2 5

That samples every two seconds, five times, then exits and prints an Average: line summarizing the whole run. Without the count it runs until you press Ctrl+C.

Per-process memory and page faults: pidstat -r

bash
pidstat -r 2

-r reports memory utilization and page faults:

code
07:16:10      UID       PID  minflt/s  majflt/s     VSZ     RSS   %MEM  Command
07:16:12     1000      4821    142.00      0.00 1284560  398204   4.91  node

RSS (resident set size, in kB) is the real memory the process holds in RAM and is usually the number you care about. VSZ is the virtual size, which is almost always larger and far less meaningful. The interesting columns are the fault rates: majflt/s is major faults per second, meaning the kernel had to hit disk to satisfy the page. A nonzero majflt/s that stays up is a process thrashing against a memory shortfall, and it correlates with the box swapping.

Per-process disk I/O: pidstat -d

bash
pidstat -d 2

-d reports I/O statistics (kernels 2.6.20 and later):

code
07:18:44      UID       PID   kB_rd/s   kB_wr/s kB_ccwr/s iodelay  Command
07:18:46        0       338      0.00   1840.00      0.00       7  jbd2/sda1-8
07:18:46     1000      4821      0.00    512.00      0.00       2  node

kB_rd/s and kB_wr/s are read and write throughput per second. iodelay is the time the task was blocked waiting on I/O, measured in clock ticks, and it is the column that tells you whether a process is bottlenecked on disk versus merely doing a lot of I/O comfortably. This is the per-process answer that iostat cannot give you: iostat shows the device is busy, pidstat -d shows who is making it busy. For the device-level and whole-system view, see the iotop command, which sorts processes live by I/O the way top sorts by CPU.

Watch one process: -p

The flag that makes pidstat genuinely useful for debugging is -p, which scopes the report to specific PIDs:

bash
pidstat -u -r -d -p 4821 1

That traces CPU (-u), memory (-r), and I/O (-d) for PID 4821, once per second, until you stop it. Combining the activity flags prints them as separate columns on the same lines, so you get a unified per-second view of one process across all three resources. This is what I leave running in a second terminal while I reproduce a performance problem.

-p takes a comma-separated list (-p 4821,4822), the keyword ALL for every task the system manages, or SELF for the pidstat process itself. -p ALL is noisy on a busy host; pair it with -C to filter by command name:

bash
pidstat -u -C nginx 2

-C keeps only tasks whose command name matches the string, which is treated as a regular expression. pidstat -u -C 'nginx|php-fpm' 2 watches both web tiers at once.

Threads, not just processes: -t

By default pidstat aggregates a process across its threads. When a single multithreaded process is the suspect and you need to know which thread is hot, add -t:

bash
pidstat -t -u -p 4821 1

-t adds TGID (the thread group, i.e. the process) and TID (the individual thread) columns and reports each thread separately. A row with a TGID and a dash for TID is the process total; rows with a TID are its threads. This is how you find the one runaway thread inside an otherwise healthy-looking process.

Useful flags at a glance

FlagWhat it reports
-uCPU utilization (%usr, %system, %wait, %CPU)
-rMemory and page faults (RSS, VSZ, minflt/s, majflt/s)
-dDisk I/O (kB_rd/s, kB_wr/s, iodelay)
-wContext-switch rate (voluntary and involuntary)
-p PIDScope to a PID list, or ALL / SELF
-tBreak processes down into their threads
-C commFilter to tasks whose command name matches comm (regex)
-lShow the full command line, not just the process name
-hOne line per sample, no Average: block (parser-friendly)

-h is worth calling out: it prints everything horizontally with no averages, which is the form to pipe into awk or a log when you want to chart pidstat output later.

When pidstat is not the right tool

pidstat is a sampler, so it is excellent for "watch this process over the next minute" and poor for "what happened five minutes ago". For after-the-fact analysis you want sar (also from sysstat), which records to disk on a schedule and lets you replay any window. For a live, sorted, interactive view, top and htop are still better for at-a-glance triage; pidstat wins once you know which process you care about and want a clean numeric trace of it.

It is also strictly a per-process tool. If the question is about network throughput rather than CPU, memory, or disk, pidstat has nothing to say. For watching interface bandwidth in real time, reach for the nload command instead. And for whole-device I/O saturation independent of any one process, iostat -x is the companion to pidstat -d.

One distro caveat: on Debian and Ubuntu the sysstat collector service (sar history) is disabled by default after install. That does not affect pidstat interactive sampling, which works the moment the package is installed, but it does mean sar has no history to show until you enable it in /etc/default/sysstat.

See also

FAQ

Sources

Authoritative references this article was fact-checked against.

TagspidstatsysstatCLILinuxPerformanceMonitoringSystem Administration

Found this useful? Pass it on.

Copied

Ishan Karunaratne

Tech Architect · Software Engineer · AI/DevOps

Tech architect and software engineer with 20+ years building software, Linux systems, and DevOps infrastructure, and lately working AI into the stack. Currently Chief Technology Officer at a healthcare tech startup, which is where most of these field notes come from.

Keep reading

Related posts

Using AI with WP-CLI for Faster WordPress Operations

The WP-CLI patterns that compose well with AI assistants: multi-step plans with checkpoint approval, generated one-off scripts, database surgery, content migrations at scale, and what to never delegate.