В этом уроке мы закрываем этот разрыв. Берём перегруженное устройство и находим конкретного виновника - тот PID, который реально пишет и читает. Инструменты простые и есть почти везде: iotop, pidstat -d и счётчики в /proc. А в конце разберём, чем эту классику в 2026 году дополняет eBPF (biotop/biosnoop), который уже стал стандартом для глубокой атрибуции. Если ты хоть раз гуглил "iotop" или "кто грузит диск linux" - это ровно про то.
Откуда вообще берутся цифры по io процесса
Сначала немного механики, без неё цифры будут магией. Ядро Linux считает дисковый ввод-вывод на двух уровнях, и их важно не путать.
Первый уровень - системные вызовы. Когда программа делает read() или write(), ядро складывает байты в счётчики rchar и wchar. Это "сколько приложение попросило". Но запрос на запись почти никогда не идёт сразу на диск: он оседает в page cache - оперативной памяти, которую ядро использует как буфер. Программа уже думает, что записала, а физически данные ещё лежат в RAM и полетят на блин/флеш позже, при сбросе (writeback).
Второй уровень - реальный блочный io. Тут живут счётчики read_bytes и write_bytes: это сколько байт по-настоящему ушло в storage. Вот эти цифры и совпадают с тем, что показывает iostat на устройстве.
Почему это критично для новичка: процесс может "писать" гигабайты в секунду через write(), а диск при этом почти спит - всё ушло в кеш. И наоборот: процесс давно ничего не пишет, а write_bytes растут - это ядро лениво сбрасывает накопленный кеш на диск. Поэтому фактический io процесса и то, что просило приложение, - две разные величины. Запомни связку: rchar/wchar - просьба приложения, read_bytes/write_bytes - реальная работа диска.
Посмотреть эти счётчики можно руками, без всяких утилит, прямо из /proc:
Код: Выделить всё
sudo cat /proc/$(pgrep -n mysqld)/ioКод: Выделить всё
rchar: 10543219712
wchar: 8732190455
syscr: 2841902
syscw: 1992011
read_bytes: 4096000000
write_bytes: 3221225472
cancelled_write_bytes: 16777216
iotop - top по диску: кто грузит диск прямо сейчас
iotop - это интерактивный монитор, по духу как top, только не про CPU, а про диск. Требует root (читает аккаунтинг по всем процессам через интерфейс taskstats ядра), поэтому запускаем через sudo:
Код: Выделить всё
sudo iotopСразу полезный режим, чтобы не отвлекаться на спящие процессы:
Код: Выделить всё
sudo iotop -oPaКод: Выделить всё
Total DISK READ: 45.20 M/s | Total DISK WRITE: 12.10 M/s
Current DISK READ: 45.10 M/s | Current DISK WRITE: 11.80 M/s
PID PRIO USER DISK READ DISK WRITE SWAPIN IO> COMMAND
3412 be/4 mysql 42.00 M/s 8.50 M/s 0.00 % 92.30 % mysqld
1180 be/4 root 2.10 M/s 0.00 B/s 0.00 % 5.10 % rsync ...
2207 be/4 www 0.00 B/s 3.60 M/s 0.00 % 1.20 % php-fpmВажный нюанс 2026 года, на котором спотыкаются все. Начиная с ядра 5.14 механизм delay accounting (он и питает колонки SWAPIN и IO>) по умолчанию ВЫКЛЮЧЕН - его отключили из-за накладных расходов. Поэтому на свежей Ubuntu, Debian, Astra или RED OS свежий iotop при старте ругнётся, что CONFIG_TASK_DELAY_ACCT не активен, а колонки SWAPIN и IO> будут пустыми. Лечится одной строкой:
Код: Выделить всё
sudo sysctl kernel.task_delayacct=1Управление: стрелками влево/вправо меняешь колонку сортировки, r переворачивает порядок, o переключает режим "только активные", p - агрегацию по процессам/потокам. Выход - q.
pidstat -d - та же атрибуция io, но в скрипты и логи
iotop хорош, когда ты сидишь у терминала и смотришь живьём. Но он интерактивный, его не положишь в cron и не отдашь в лог. Для неинтерактивной атрибуции дисковой нагрузки на процессы есть pidstat из пакета sysstat (тот же, откуда iostat). Связка pidstat диск - рабочая лошадка для автоматики, и, в отличие от iotop, root тут не обязателен для своих процессов.
Код: Выделить всё
pidstat -d 1Код: Выделить всё
15:42:07 UID PID kB_rd/s kB_wr/s kB_ccwr/s iodelay Command
15:42:08 1001 3412 43008.00 8704.00 0.00 145 mysqld
15:42:08 0 1180 2150.00 0.00 0.00 3 rsync
15:42:08 33 2207 0.00 3686.40 0.00 0 php-fpmЧтобы не разгребать всех подряд, фильтруй по команде:
Код: Выделить всё
pidstat -d 1 -C mysqldКод: Выделить всё
pidstat -dl 1 60 >> /var/log/io_who.logeBPF в 2026: biotop и biosnoop для глубокой атрибуции
iotop и pidstat отвечают на вопрос "кто и сколько". Но они работают через периодический опрос счётчиков и теряют короткоживущие процессы (бэкап на 0.5 секунды просто не попадёт в кадр) и не показывают задержку каждой отдельной операции. В 2026 году стандартный ответ на это - eBPF-инструменты из наборов bcc и bpftrace, которые трассируют сам блочный слой ядра. Ставятся пакетом bpfcc-tools (Debian/Ubuntu/Astra) или bcc-tools (RHEL/RED OS); нужно ядро не древнее 4.x, на современных дистрибутивах всё уже есть.
biotop - это буквально top по блочному io, но построенный на трассировке, а не на опросе. Он точно ловит даже мелкие и короткие операции:
Код: Выделить всё
sudo biotop-bpfccКод: Выделить всё
PID COMM D MAJ MIN DISK I/O Kbytes AVGms
3412 mysqld R 259 0 nvme0n1 842 421000 0.38
1180 rsync R 259 0 nvme0n1 21 2150 0.51
2207 php-fpm W 259 0 nvme0n1 45 3686 1.92Код: Выделить всё
sudo biosnoop-bpfccКод: Выделить всё
TIME(s) COMM PID DISK T SECTOR BYTES LAT(ms)
0.000000 mysqld 3412 nvme0n1 R 1048576 16384 0.42
0.004210 jbd2/nvme0 289 nvme0n1 W 88210432 4096 8.71Типичные грабли и заблуждения
- Пустые колонки IO> и SWAPIN в iotop (и нулевой iodelay в pidstat). Самая частая засада на свежих ядрах: с 5.14 delay accounting выключен по умолчанию. Не баг - включи sudo sysctl kernel.task_delayacct=1 (или Ctrl-T в iotop). Колонки DISK READ/WRITE работают и без этого.
- "iotop не запускается". Чаще всего забыли sudo - без root утилита не получит аккаунтинг по чужим процессам через taskstats.
- Путают page cache и реальную запись. Видят в приложении "записал 5 ГБ", а диск спокоен. Это норма: write() ушёл в кеш. Реальная нагрузка проявится при сбросе (можно увидеть всплеск write_bytes и работу ядерных потоков kworker и jbd2 при flush). Не ищи виновника там, где работает кеш.
- Смотрят только на мегабайты и игнорируют IO>/iodelay/AVGms. Процесс с маленьким потоком, но огромным временем ожидания io грузит диск мелкими случайными запросами хуже, чем тот, кто льёт крупным последовательным потоком. На HDD это критично, да и на дешёвом SSD тоже.
- Берут /proc/PID/io как скорость. Там накопительные счётчики с момента старта, а не B/s. Для скорости нужны два замера во времени - или просто бери pidstat/iotop/biotop, они это считают сами.
- Винят kworker или jbd2. Это не "вирус", а ядерные потоки, которые сбрасывают чужой кеш и ведут журнал ФС. Настоящий виновник - тот, кто наполнил этот кеш; ищи его по wchar и по логике приложения, а не по имени ядерного треда.
Делается за пять минут на любой тестовой машине (не на проде).
- Включи аккаунтинг, иначе не увидишь IO>: sudo sysctl kernel.task_delayacct=1.
- Открой два терминала. В первом запусти sudo iotop -oPa.
- Во втором создай управляемую нагрузку записи: dd if=/dev/zero of=/tmp/testio bs=1M count=2000 oflag=direct. Флаг oflag=direct заставляет писать мимо page cache, прямо на диск - так ты увидишь реальный io. Найди dd в iotop, посмотри его DISK WRITE и IO>.
- Теперь убери oflag=direct и повтори: dd if=/dev/zero of=/tmp/testio bs=1M count=2000. Заметь, что DISK WRITE в iotop теперь скачет неравномерно (кеш копит и сбрасывает порциями), а не ровным потоком.
- Параллельно во втором терминале запусти pidstat -d 1 -C dd и сравни kB_wr/s с тем, что в iotop. Должны совпадать по порядку.
- Посмотри сырые счётчики: sudo cat /proc/$(pgrep -n dd)/io. Сравни wchar (что попросило dd) и write_bytes (что реально ушло на диск).
- Если есть eBPF-тулзы: в третьем терминале запусти sudo biotop-bpfcc и посмотри на колонку AVGms во время dd - сравни задержку при oflag=direct и без него.
- Прибери за собой: rm /tmp/testio.
- Чем отличаются wchar и write_bytes в /proc/PID/io и почему первое может быть сильно больше второго?
- В iotop процесс показывает скромные DISK READ, но IO> около 100%. О чём это говорит и почему такой процесс опасен для диска?
- Ты запустил iotop на свежем сервере, а колонки SWAPIN и IO> пустые. В чём причина на ядрах 5.14 и новее и как это исправить?
- Зачем нужен pidstat -d, если есть iotop, и что дополнительно дают eBPF-инструменты biotop/biosnoop?
iostat говорит, что устройство перегружено; iotop и pidstat -d говорят, КТО его грузит - это и есть рабочая связка диагностики дисковой нагрузки. iotop - интерактивный top по диску (нужен root, смотри колонки DISK READ/WRITE, SWAPIN и особенно IO>; на современных дистрибутивах это iotop-c). pidstat -d 1 - то же по процессам, но без интерактива, для скриптов и логов (kB_rd/s, kB_wr/s, iodelay). На 2026 помни про delay accounting: с ядра 5.14 он выключен по умолчанию, и без kernel.task_delayacct=1 колонки IO>/SWAPIN/iodelay будут пустыми. Для глубокой атрибуции и латентности отдельных операций бери eBPF: biotop и biosnoop. Под капотом всё опирается на счётчики /proc/PID/io, где rchar/wchar это просьба приложения, а read_bytes/write_bytes - реальный диск. Всегда держи в голове page cache: записал в кеш не значит записал на диск. Нашёл PID - дальше смотри strace/lsof по этому процессу и думай, что он там делает.