Where to start First steps track · 15 stages
Linux Labs

iostat and pidstat — which disk is busy and which process loads it

8 min sysstat 12.6 · Ubuntu 24.04

iostat shows disk load — requests, latency, %util; pidstat shows CPU, memory and disk usage per process.

Cheat sheet

8 commands Printable version
  • iostat -dxsy 1 5 disks: tps, kB/s, await, aqu-sz, %util
  • iostat -x vda 1 3 all fields of one disk
  • sudo pidstat -dl 1 5 who reads and writes, full command
  • pidstat -u 1 5 CPU per process, %wait
  • pidstat -r 1 5 memory and page faults per process
  • pidstat -d -p 1151 1 5 one process
  • ionice -p 1151 the process's disk priority class
  • sudo ionice -c3 -p 1151 let others use the disk first (idle)

Overview

iostat and pidstat come from the sysstat package. iostat looks at devices: how many requests, how much data, how long the answers took. pidstat looks at processes: who spends CPU, memory and disk.

Both take an interval and a report count: iostat 1 5 is five reports one second apart. Without an interval there is one report with averages since system boot. The first iostat report with an interval also holds averages since boot; -y skips it.

iostat -x: device fields

iostat -x prints a couple of dozen columns. On the training machine dd was writing to disk in 4 KiB blocks bypassing the cache (oflag=direct):

Field Value Meaning
w/s 6844.00 write requests per second
wkB/s 27376.00 KiB written per second
w_await 0.13 average write request time, ms: queue plus device work
wareq-sz 4.00 average request size, KiB
aqu-sz 0.86 average queue length at the device
%util 84.00 share of time the device was busy

The same fields with r are for reads (r/s, rkB/s, r_await), with d for discards, with f for device cache flushes.

iostat -dxs: short form

-s squeezes -x into one narrow line, -d drops the CPU block:

Device tps kB/s rqm/s await areq-sz aqu-sz %util
vda 12540.00 50160.00 0.00 0.06 4.00 0.77 70.20

tps is requests per second (reads and writes together), rqm/s is requests merged with neighbours before reaching the device.

Reading %util and await

%util 100 on a disk that serves one request at a time (an HDD) means the limit. Disks that take many requests at once (SSD, NVMe, virtual disks) can do more at 100 %. The real sign of overload is growing await and aqu-sz: requests wait in a queue. On the training machine at %util 84 a request waited 0.13 ms, and the queue was below one.

pidstat -d: who loads the disk

bash
sudo pidstat -d 2 1
UID PID kB_rd/s kB_wr/s kB_ccwr/s iodelay Command
0 1151 0.00 32113.43 0.00 0 dd
Field Meaning
kB_rd/s read from disk, KiB/s
kB_wr/s written or handed to the cache for writing, KiB/s
kB_ccwr/s cancelled writes: the file was deleted before data reached the disk
iodelay how many ticks the process waited for I/O

Only root can read another user's I/O counters. The same dd running as root is invisible without sudo: pidstat -d prints only headers.

ps shows which service the process belongs to:

text
student@ubuntu:~$ ps -o pid,unit,comm -p 1211
    PID UNIT                            COMMAND
   1211 ssh.service                     sshd

pidstat -u: who waits for the CPU

bash
pidstat -u 1 2      # the Average lines, some columns omitted
UID PID %usr %system %wait %CPU Command
1000 975 48.76 0.00 51.24 48.76 stress-ng-cpu
1000 976 48.76 0.00 51.24 48.76 stress-ng-cpu

Two processes on one CPU: each worked half the time (%usr) and waited for its turn the other half (%wait). High %wait means more R processes than CPUs (see load-average).

pidstat -r shows memory per process: minflt/s and majflt/s are page accesses served without and with the disk, plus VSZ, RSS, %MEM. pidstat -w shows task switches: cswch/s (the process gave up the CPU itself, waiting for I/O or an event) and nvcswch/s (the CPU was taken away).

ionice: disk priority

ionice sets a process's disk access class: realtime (-c1), best-effort (-c2, the default) and idle (-c3 — only when nobody else needs the disk).

text
student@ubuntu:~$ ionice -p 1
none: prio 0
student@ubuntu:~$ sudo ionice -c3 -p $$; ionice -p $$
idle

none means no class was set, and the process runs as best-effort. The class is honoured by the disk's I/O scheduler. On the training machine vda uses mq-deadline, loop devices use none, and there priorities do nothing:

text
student@ubuntu:~$ cat /sys/block/vda/queue/scheduler
none [mq-deadline]

Options

15

The main options of both commands are in the table below, in the groups iostat and pidstat. The full list is in man iostat and man pidstat.

Option What it does Example
-x Extended device statistics — requests, await latency, aqu-sz queue, %util. iostat -x vda 1 3
-d I/O per process — kB_rd/s, kB_wr/s, iodelay; other users' processes are visible to root only. sudo pidstat -d 1 5
-u CPU per process — %usr, %system, %wait (waited for a CPU), %CPU. pidstat -u 1 5
-d Devices only, without the avg-cpu block. iostat -dx 1 3
-s Short form with -x — tps, kB/s, await, aqu-sz, %util in one narrow line. iostat -dxs 1 3
-y Skip the first report, the averages since boot. iostat -dxsy 1 3
-z Omit devices with no activity during the interval. iostat -dxz 1
-r Memory per process — page faults per second, VSZ, RSS, %MEM. pidstat -r 1 5
-p PID Only the given processes; ALL means all, idle ones included. pidstat -d -p 1151 1 5
-l Show the whole command line with arguments. sudo pidstat -dl 1 5
-p DEV Statistics per partition of the device. iostat -p vda
-h Sizes with units — k, M, G; the device name at the end of the line. iostat -h
-w Task switches — voluntary cswch/s and involuntary nvcswch/s. pidstat -w 1 5
-t Statistics per thread of the process too. pidstat -t -p 1 1 3
-v Number of threads and open descriptors of processes. pidstat -v

Nothing found. Try a shorter query.

Recipes

3

Find the process that loads the disk

bash
vmstat 1 5                # high wa, b above 0?
iostat -dxsy 1 3          # which disk: %util, await
sudo pidstat -dl 1 5      # which process: kB_wr/s, kB_rd/s

The general order for all resources is in system-monitoring.

Lower a process's disk priority

bash
sudo ionice -c3 -p 1151           # a running process
ionice -c3 nice -n 19 tar czf /var/tmp/b.tgz /srv   # at start

nice lowers the CPU share, ionice the disk share; a backup on a busy server needs both.

Watch one process

bash
pidstat -u -r -d -p 1151 1     # CPU, memory and disk every second

Troubleshooting

5

pidstat -d shows nothing

Two reasons. The process belongs to another user — without sudo its counters cannot be read. The process read and wrote nothing during the interval — pidstat prints only active ones; -p <PID> shows an idle one too.

The first iostat line looks different

It holds averages since boot, including the whole boot of the system. Look at the following reports or add -y.

%util is 100, yet everything works

The device serves requests in parallel, and %util counts only the time when anything at all was busy. Look at await and aqu-sz.

ionice does not help

The none disk scheduler ignores classes: check /sys/block/<disk>/queue/scheduler. Writes through the page cache reach the disk later on behalf of the kernel, and the process class affects them little.

iotop: command not found

iotop is not installed on the training machine. sudo pidstat -d 1 gives the same per-process picture.

See also

Loading…