В этом уроке мы соберём вместе всё, что разбирали раньше: top, ps, strace, perf, iostat, ss, journalctl - и добавим современный слой 2026 года: PSI (pressure stall information) и eBPF-инструменты, которые в проде давно заменили тяжёлый strace. Не как набор отдельных команд, а как единый маршрут. Цель - за несколько минут понять, ГДЕ тормозит, а не гадать. Дальше копаем точечно. Это сквозной кейс и дерево решений, к которому ты сможешь возвращаться каждый раз, когда сервер тормозит, а причина неясна.
Первые 60 секунд: где именно болит
Когда приложение тормозит на linux, первое - не лезть внутрь приложения. Сначала локализуем подсистему. Есть четыре подозреваемых: CPU, память, диск, сеть. Задача первой минуты - вычеркнуть три из них и оставить один.
Открываем общую картину:
Код: Выделить всё
$ uptime
03:14:22 up 40 days, 2:11, 3 users, load average: 18.42, 12.10, 6.55
Дальше - vmstat, он за один экран показывает почти всё. Первая строка vmstat - усреднение с момента загрузки, её игнорируем и смотрим со второй:
Код: Выделить всё
$ vmstat 1 5
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
r b swpd free buff cache si so bi bo in cs us sy id wa st
14 3 0 198432 10220 880144 0 0 120 980 5200 8100 22 9 4 65 0
- r - сколько процессов готовы бежать на CPU прямо сейчас (в очереди на выполнение). Если r стабильно больше числа ядер - упёрлись в процессор.
- b - сколько процессов заблокированы в непрерывном ожидании (чаще всего io). Растёт b - смотри на диск.
- si/so - своп ин/аут в КБ/с. Если тут не нули и они шевелятся - памяти не хватает, система свопится, отсюда дикие тормоза.
- wa (в блоке cpu) - процент времени, когда CPU простаивал в ожидании io. wa 65 в примере - красный флаг: процессор не занят, он ждёт диск.
- us/sy - время в пользовательском коде и в ядре. Высокий us - работает само приложение. Высокий sy - много системных вызовов, ядро перегружено (часто это шквал мелких read/write или контекст-свитчей).
- id - простой. Если id около нуля и при этом us высокий - CPU-bound. Если id низкий, а wa высокий - io-bound.
- st (steal) - время, которое гипервизор украл у твоей VM в пользу соседей. Ненулевой st на облачной машине - твой сервер тормозит не сам по себе, а из-за шумного соседа. Частая и неочевидная причина в облаке.

PSI: точнее, чем load average (актуально на 2026)
vmstat показывает загрузку, но не отвечает на вопрос "насколько сильно из-за этого тормозят задачи". На это отвечает PSI - pressure stall information, штатная фича ядра с 4.20, на современных дистрибутивах 2026 года (с systemd и cgroup v2) включена по умолчанию. Это лучший быстрый индикатор насыщения, который часто видит проблему раньше обычных метрик утилизации.
Код: Выделить всё
$ cat /proc/pressure/io
some avg10=78.20 avg60=61.40 avg300=22.10 total=98473321
full avg10=64.10 avg60=50.02 avg300=18.33 total=71203991
Кто виноват: состояние процесса в top и ps
Подсистему нашли. Теперь - какой процесс и в каком он состоянии. Состояние процесса - самая недооценённая подсказка для новичка. Буква в колонке STAT говорит, чем процесс занят прямо сейчас.
Код: Выделить всё
$ top -o %CPU
top - 03:15:01 up 40 days, load average: 18.42, 12.10, 6.55
%Cpu(s): 22.1 us, 9.0 sy, 0.0 ni, 4.0 id, 64.9 wa, 0.0 hi, 0.0 si
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
4123 appuser 20 0 2410000 512000 18000 D 12.3 12.7 88:12.4 java
4567 appuser 20 0 210000 44000 6000 R 98.6 1.1 12:03.1 worker
- R (running/runnable) - процесс жрёт CPU или готов его жрать. Если виновник в R и %CPU под 100 - это вычислительная нагрузка, идём смотреть КОД и системные вызовы.
- D (uninterruptible sleep) - процесс застрял в непрерывном ожидании, почти всегда это io: ждёт диск, NFS или иногда заблокирован страничной подкачкой. Процесс в D нельзя убить даже kill -9, пока он не дождётся события. Много D-процессов плюс высокий wa = диск, без вариантов.
- S (interruptible sleep) - спит, ждёт события: сокет, таймер, мьютекс. Само по себе нормально, большинство процессов в S. Но если "тормозящий" процесс в S и ничего не делает - возможно, он ждёт ответа по сети или висит на блокировке.
- Z (zombie) - завершился, но родитель не забрал статус. Сам по себе не тормозит, но толпа зомби - признак бага в управлении процессами.
- I (idle) - заспавший ядерный поток, шум, его игнорируем.
Запиши себе PID кандидатов. Дальше работаем точечно по ним.
На чём именно застрял: strace, perf и eBPF
Мы знаем процесс и его состояние. Теперь - на чём конкретно он стоит. Тут развилка по состоянию.
Процесс в R и ест CPU. Хочется быстро глянуть, в каких системных вызовах он сидит. Классика - strace с подсчётом:
Код: Выделить всё
$ sudo strace -c -p 4567
^C
% time seconds usecs/call calls errors syscall
------ ----------- ----------- --------- --------- ----------------
71.20 0.412345 82 5012 12 futex
18.04 0.104500 26 4002 read
6.10 0.035300 17 2050 write
4.66 0.027000 54 500 epoll_wait
------ ----------- ----------- --------- --------- ----------------
100.00 0.578145 11564 24 total
Важная оговорка про strace (актуально на 2026). strace работает через ptrace и останавливает процесс на каждом системном вызове. Это замедляет цель в разы, по замерам - от нескольких до сотни раз. На тестовом стенде это нормально, но на нагруженном проде strace на горячий процесс сам по себе усугубит тормоза. Поэтому в 2026 на проде вместо strace бери низконакладные инструменты на eBPF:
- perf trace -p 4567 - тот же список syscall, что у strace, но через perf-буферы, оверхед обычно меньше процента, безопасно в проде.
- bpftrace для точечного подсчёта, например за 10 секунд по всему процессу: sudo bpftrace -e 'tracepoint:raw_syscalls:sys_enter /pid==4567/ { @[probe]=count(); }'.
- Готовые bcc/bpftrace-инструменты пакета bpfcc-tools: syscount -p 4567 (топ syscall), funccount, offcputime.
Если же syscall почти нет, а %CPU высокий - время уходит в самом коде. Тогда perf:
Код: Выделить всё
$ sudo perf top -p 4567
Samples: 48K of event 'cpu-clock'
Overhead Shared Object Symbol
41.20% app.bin [.] json_parse_value
17.05% libc.so.6 [.] __memmove_avx_unaligned
9.80% app.bin [.] hash_lookup
Процесс в D и высокий wa. Это диск. Открываем iostat:
Код: Выделить всё
$ iostat -x 1 3
Device r/s w/s rkB/s wkB/s r_await w_await aqu-sz %util
sda 12.0 840.0 480.0 92000.0 2.10 118.50 42.30 99.80
Кто пишет - покажет iotop, а на 2026 точнее покажут eBPF-инструменты:
Код: Выделить всё
$ sudo iotop -oP
PID PRIO USER DISK READ DISK WRITE COMMAND
4123 be/4 appuser 0.00 B/s 88.00 M/s java -jar app.jar
Процесс в S и приложение ждёт сеть. Подсистема - сеть. Смотрим сокеты:
Код: Выделить всё
$ ss -tanp state established
Recv-Q Send-Q Local Address:Port Peer Address:Port
0 524288 10.0.0.5:8080 10.0.3.9:51324 users:(("app",pid=4567,fd=23))
Код: Выделить всё
sudo tcpdump -ni eth0 host 10.0.3.9 and port 8080 -c 50Логи параллельно и типичные грабли
Пока копаешь метрики, держи открытым второй терминал с логами. Часто причина уже написана прямым текстом, а ты её ищешь профайлером.
Код: Выделить всё
$ journalctl -u myapp.service -n 50 --no-pager
$ journalctl -k -p err --since "10 min ago"
$ sudo dmesg -T | tail -30
Типичные заблуждения, на которых застревают:
- "Высокий load average - значит, не хватает CPU". Нет. load в Linux включает процессы в состоянии D. Высокий load + высокий wa = это диск, а не процессор.
- "%util 100% - диск умер". Для одиночного HDD - перегрузка. Для NVMe, SSD и RAID %util не отражает предел, они работают параллельно. Ориентируйся на await и aqu-sz, а ещё лучше на biolatency.
- "Память забита, надо чистить". Колонки buff/cache в free - это кэш, система отдаст его приложению по требованию. Тревога - только когда растёт swap (si/so в vmstat) и падает available.
- "Запущу strace на проде - быстро гляну". strace тормозит цель через ptrace в разы. На нагруженном проде используй perf trace или eBPF (syscount, offcputime).
- Менять несколько вещей разом. Покрутил sysctl, перезапустил сервис, поправил конфиг - стало лучше. Что помогло? Непонятно. Меняй по одному.
- Гадать вместо измерения. Формулируй гипотезу ("тормозит из-за диска, потому что wa 65, io.pressure 78 и процессы в D") и проверяй её конкретной командой. Не подтвердилось - следующая гипотеза.
Мини-лаба: повтори руками прямо сейчас
Создадим управляемую нагрузку и пройдём по дереву решений. Понадобится stress-ng (есть в репозиториях Ubuntu/Debian/RHEL/Fedora; в Astra Linux и RED OS тоже доступен). eBPF-инструменты - в пакете bpfcc-tools (Debian/Astra) или bcc-tools (RHEL/RED OS).
- Нагрузка на CPU: . В соседнем терминале
Код: Выделить всё
stress-ng --cpu 2 --timeout 60sиКод: Выделить всё
vmstat 1. Убедись: r растёт, us высокий, id около нуля, процесс в R. ГляньКод: Выделить всё
top- avg10 поползёт вверх. Это CPU-bound.Код: Выделить всё
cat /proc/pressure/cpu - Нагрузка на диск: . Смотри
Код: Выделить всё
stress-ng --hdd 2 --timeout 60sиКод: Выделить всё
iostat -x 1. Поймай высокий wa, процессы в D, растущий w_await и aqu-sz,Код: Выделить всё
topс высоким avg10. Это io-bound. Если есть biolatency - запустиКод: Выделить всё
cat /proc/pressure/ioи посмотри гистограмму.Код: Выделить всё
sudo biolatency - Возьми PID активного процесса и сними профиль вызовов щадяще: на пару секунд (или
Код: Выделить всё
sudo perf trace -p PIDна тестовой машине). Прочитай таблицу по колонкам, найди топовый syscall.Код: Выделить всё
strace -c -p PID - Глянь свежие ошибки ядра: .
Код: Выделить всё
sudo dmesg -T | tail -20
Контрольные вопросы
- В vmstat ты видишь wa 70, id 5, us 10, процессы в D, а /proc/pressure/io показывает some avg10=80. В какую подсистему упёрлись и какой инструмент следующий?
- Чем отличается процесс в состоянии R от процесса в D, и о чём говорит каждое при разборе тормозов?
- Профиль показал, что 70% времени уходит в futex. Это упор в CPU, в диск или во что-то ещё? Каким eBPF-инструментом это подтвердить?
- Почему на нагруженном проде нежелательно запускать strace на горячий процесс и чем его заменить в 2026?
"Тормозит" - это не диагноз. Сначала за 60 секунд локализуй подсистему (vmstat: r, b, wa, si/so, st; и /proc/pressure для cpu/io/memory), потом найди процесс и его состояние (top/ps: R/D/S), потом докопайся до точки (perf trace или strace -c для syscall, perf top для кода, iostat/biolatency/iotop для диска, ss/tcpdump/tcplife для сети), и всё это параллельно с логами (journalctl, dmesg). На 2026 предпочитай низконакладные eBPF-инструменты вместо тяжёлого strace в проде. Двигайся по гипотезам, меняй по одной вещи за раз, записывай шаги. Это маршрут, который работает на любом сервере: от ноутбука до прода и от железа до контейнера.