✻ Урок 1.3 · Тема 1: Linux для нагрузочника
Процессы и ресурсы: CPU, память, диск, load average
Содержание урока
Зачем это нужно
Я много лет смотрю, что творится на серверах под нагрузкой, и сейчас покажу, с чего начинаю. Представь твой первый рабочий день перед распродажей. Нагрузочный тест закончился, в отчёте две строки: «ответы стали медленными, а на 200 пользователях сервис упал». Руководитель говорит: «Глянь, что там с машиной».
Со мной было так в первый год. Я полдня читал код сервиса и искал в нём ошибку. Потом зашёл коллега, набрал три команды и сказал: «Память кончилась, система гоняет её на диск». Код трогать не требовалось. С тех пор я начинаю с машины, а не с кода.
У машины четыре ресурса, которые могут кончиться. Процессор (CPU) считает, память (RAM) хранит то, с чем программы работают сейчас, диск хранит надолго, сеть связывает машины. Запущенная программа называется процессом, и каждый берёт свою долю этих ресурсов. Ресурс, который кончился первым и не даёт сервису ускориться, называют узким местом (bottleneck). Поиск узких мест это половина работы нагрузочного тестировщика, на нём построена тема 11.
Сеть разберём в уроке 1.4, здесь процессор, память и диск.
Шаг проекта: ты ставишь наблюдателей (sysstat), намеренно нагружаешь свою машину процессором, памятью и диском и учишься узнавать каждую перегрузку в top, free, vmstat и iostat. Результат это шпаргалка ~/perf-lab/01-linux/resource-notes.md: что смотреть, как выглядят норма и перегрузка. Она пригодится, когда стенд «Магазин» будет работать под нагрузкой.
Что нужно знать
- Терминал,
man,--help,sudo apt install: урок 1.1. Паспорт машины (nproc,free -h) оттуда будет нашей точкой отсчёта. - Конвейеры
|,grep,sort,awk,head: урок 1.2. Мы будем выделять нужное из длинного выводаpsиtop. - Глубже: соответствующий урок курса DevOps разбирает те же ресурсы с точки зрения администратора. Здесь всё нужное для курса собрано полностью.
Картина целиком
Возьми кухню ресторана. Повара это ядра процессора: каждый в один момент готовит одно блюдо. Рабочий стол это память: быстрый, но маленький. Кладовка это диск: вместительная, но ходить туда долго. Заказы это процессы, и каждый хочет повара, место на столе и иногда визит в кладовку.
Кухня тормозит по четырём причинам. Заказов больше, чем поваров. Стол завален, заготовки носят в кладовку. Повара стоят у двери кладовки и ждут. Или в кладовке нет места.
flowchart TD
A["Сервис тормозит"] --> B{"Заказов в очереди<br/>больше, чем поваров?"}
B -->|да| C{"Повара заняты<br/>готовкой?"}
B -->|нет| F{"На столе почти<br/>нет места?"}
C -->|да| D["Упёрлись в процессор"]
C -->|нет, ждут кладовку| E["Упёрлись в диск"]
F -->|да| G["Не хватает памяти"]
F -->|нет| H["Ищем снаружи:<br/>сеть, база"]
Два вопроса, про очередь и про место на столе, отсекают большую часть случаев. Два «нет» значат, что ждут снаружи. В конце теории схема вернётся с командами.
Теория
Процесс: что запущено на машине
Открой на ноутбуке диспетчер задач: сотня строк, половину ты не узнаёшь. На сервере то же самое, и этот список отвечает на вопрос «кто виноват».
Программа на диске это файл, как рецепт в книге: сам он ничего не делает. Когда его запустили, система загружает программу в память, выдаёт ей процессор и следит за ней. Это запущенное состояние и есть процесс: повар, который прямо сейчас готовит по рецепту. Аналогия ломается в одном: у процесса есть номер. Он называется PID (process ID), по нему к процессу обращаются: «останови 48211». Список всех процессов даёт ps aux.
yes > /dev/null &
ps aux --sort=-%cpu | head -n 3
kill %1
[1] 48211
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
student 48211 99.6 0.0 8344 640 pts/0 R 10:40 0:12 yes
root 1204 1.2 0.4 1231096 67840 ? Ssl Oct01 35:07 /usr/lib/snapd/snapd
yes бесконечно печатает букву y, а > /dev/null выбрасывает вывод (урок 1.2). Это простейшая «нагрузка на процессор»: программа ничего не ждёт. Знак & отправил её в фон: оболочка написала [1] 48211, то есть номер задания и PID. --sort=-%cpu ставит сверху самых прожорливых (минус значит «по убыванию»), head -n 3 оставляет заголовок и две строки. У yes в %CPU 99,6: одно ядро занято целиком.
Две колонки про память путают. VSZ (virtual size) это весь адрес, который процесс «заказал»: цифра бывает огромной, а используется малая часть. RSS (resident set size) это память, которую процесс реально занимает сейчас. Когда говорят «процесс занимает 400 МБ», имеют в виду RSS.
Прикинь сам: у процесса
RSS400 МБ, аVSZ4 ГБ. Сколько памяти он занимает и стоит ли волноваться?
Ответ
Он держит 400 МБ (RSS). Четыре гигабайта это зарезервированный адрес, волноваться из-за него не нужно. Подозрителен быстрый рост RSS.
В колонке STAT состояние процесса, и нужны три буквы. R (running): работает на ядре или готов работать и ждёт очереди. S (sleeping): спит и ждёт события, например запроса по сети, это нормально для большинства процессов. D (uninterruptible sleep): ждёт ответа диска и не слышит даже сигналов. Много процессов в D значит проблему с диском.
stateDiagram-v2
[*] --> Готов: запуск
Готов --> Работает: ядро дало процессор
Работает --> Готов: время вышло
Работает --> Ждёт: нужны диск или сеть
Ждёт --> Готов: дождался
Работает --> [*]: завершился
Процесс идёт по кругу «работает, ждёт, снова готов». «Готов» и «Работает» в ps оба R, «Ждёт» это S или D. Поэтому процессов в R бывает больше, чем ядер: лишние стоят в очереди.
Для любопытных: состояния T и Z, фоновые задания
T (stopped) значит «остановлен», например после Ctrl+Z. Z (zombie): процесс завершился, но родитель, который его запустил, не забрал код завершения. Ресурсов он не берёт, но много «зомби» значит ошибку в родителе.
Фоном управляют команда &, jobs, fg, bg и Ctrl+Z; PID по имени находит pgrep -a имя.
Главное: процесс это запущенная программа с номером (PID). Для расследования важны
%CPU,RSSи состояниеR,S,D.
Виновника нашли. Теперь его надо остановить, и аккуратно.
Сигналы: как остановить процесс
Программа зависла, и ты закрываешь окно. На сервере окна нет, а процесс нельзя «выключить»: его можно только попросить.
Просьба называется сигналом (signal): короткое сообщение процессу. Первый сигнал ты уже знаешь: Ctrl+C шлёт SIGINT (номер 2), «прерви работу». Второй, SIGTERM (15), посылает команда kill PID: вежливое «заверши работу». Если программа это умеет, она успевает сохранить данные и закрыть файлы, если не умеет, просто завершается.
Третий устроен иначе. SIGKILL (9), команда kill -9 PID, до процесса не доходит: систему просят убрать его немедленно. Он ничего не сохранит, это как выдернуть шнур, пока документ записывается. Поэтому порядок такой: сначала kill, и только если процесс не реагирует, kill -9.
Осторожно: процесс вправе проигнорировать kill. Все процессы с одним именем останавливает pkill имя.
Главное: процессу посылают сигнал. Сначала вежливый
kill(SIGTERM), и только потомkill -9(SIGKILL), у которого процесс ничего не успевает сохранить.
Останавливать научились. Теперь главное: куда уходит время процессора.
Процессор: ядра и куда уходит их время
На сервере сто процессов, а ядер у машины восемь. Как они делятся?
Ядро процессора (core) это «повар»: в каждый момент выполняет один процесс. Сколько их у тебя, ты записал в паспорт (урок 1.1): это число из nproc. Система выдаёт процессу кусочек времени в несколько миллисекунд и передаёт ядро следующему. Один повар обслуживает несколько столиков по очереди, но если столиков много, каждый ждёт долго: готовые процессы встают в очередь.
top и vmstat раскладывают время ядра в проценты. Важны четыре доли. us (user): работа обычных программ, твоего кода, сервиса, базы. sy (system): работа самой системы по просьбе программ, например сеть и диск. id (idle): ядру нечего делать. wa (iowait): ядро свободно, но процессы ждут диск. Последняя особенно ценна: процессор не занят, а система тормозит из-за диска.
Посмотрим на 8-ядерной машине с 12 процессами yes:
top -b -n 1 | head -n 12
top - 10:41:07 up 3 days, 2:23, 1 user, load average: 7.60, 2.20, 0.80
Tasks: 312 total, 13 running, 299 sleeping, 0 stopped, 0 zombie
%Cpu(s): 99.7 us, 0.3 sy, 0.0 ni, 0.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
MiB Mem : 15880.2 total, 9312.7 free, 3187.0 used, 3380.5 buff/cache
MiB Swap: 4096.0 total, 4096.0 free, 0.0 used. 11300.4 avail Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
48211 student 20 0 8344 640 576 R 66.7 0.0 0:41.12 yes
48212 student 20 0 8344 640 576 R 66.7 0.0 0:41.09 yes
48213 student 20 0 8344 640 576 R 66.7 0.0 0:41.05 yes
Флаг -b печатает один снимок и выходит, -n 1 задаёт один кадр. Для точных %CPU смотри второй кадр (-n 2). В Tasks 13 процессов в R: 12 наших yes и сам top. В %Cpu(s) 99,7% ушло на обычные программы, простоя нет, а wa ноль, значит диск ни при чём.
В таблице каждый yes получил 66,7% ядра. Откуда число? У восьми ядер вместе 800%, делим на 12 процессов: 800 ÷ 12 = 66,7.
Прикинь сам: на 4-ядерной машине работают 6 процессов
yes. Сколько%CPUу каждого?
У четырёх ядер вместе 400%, делим на 6: примерно 66,7.
Процент у процесса считается от одного ядра, поэтому у многопоточной программы (она запускает несколько рабочих «ниток», и каждая занимает своё ядро) %CPU бывает больше 100. В ps этот %CPU ещё и среднее за всё время жизни процесса, а в top за последние секунды. А %Cpu(s) считается от всех ядер сразу: шкалы разные. Нажми в top клавишу 1, и он покажет каждое ядро отдельно. Это важно для однопоточного сервиса (одна «нитка»): одно ядро занято на 100%, семь простаивают, общая загрузка 12% безобидна, а сервис уже упёрся в предел. Так ведёт себя сервис с одним воркером (рабочим процессом, который принимает запросы): урок 11.2.
Для любопытных: остальные колонки процессора и неприятный `sy`
st (steal) бывает на виртуальной машине (твоя виртуалка в Multipass или WSL2 тоже виртуальная): гипервизор (программа, делящая физический компьютер между виртуалками) отдал это время соседней.
Высокое sy (выше 20-30%, ориентир, не закон) значит, что ядро системы тратит время на переключение между процессами, сеть и диск, а не на твой код.
Осторожно: 100% процессора не всегда плохо. Если машина считает и очереди нет, это полезная работа. Плохо, когда готовых процессов заметно больше, чем ядер: запросы ждут. Или когда однопоточный сервис упёрся в своё ядро: остальные свободны, а его запросы стоят в очереди.
Главное: время ядра делится на
us,sy,idиwa, а 100% загрузки плохо, когда работе приходится ждать: процессов больше, чем ядер, или однопоточный сервис упёрся в своё ядро.
Процент показывает занятость, но не длину очереди. Нужна цифра, которая считает ждущих.
Load average: сколько процессов хотят работать
Касса занята на 100%. Это один неторопливый покупатель или сто человек в очереди? По проценту не понять.
Для очереди в Linux есть load average («средняя нагрузка»): среднее число процессов, которые работают, ждут ядро (R) или ждут диск (D). Если касс четыре и людей четыре, никто не ждёт. Если людей двенадцать, восемь стоят в очереди. Число показывают uptime, top и мониторинг, и чисел три: среднее за последние 1, 5 и 15 минут.
load average: 9.87, 5.12, 2.31
Пусть uptime показал 9.87, 5.12, 2.31. За минуту в среднем 9,87 процесса хотели процессор, за пять 5,12, за пятнадцать 2,31. Первое число больше второго, а то больше третьего: нагрузка растёт.
Число надо сравнить с числом ядер (nproc). Возьмём 8 ядер (тогда 9,87 это чуть больше 1,2 на ядро). Load average 8: все ядра заняты, очереди нет, это предел без запаса. Load average 12: четыре процесса ждут, и 12 ÷ 8 даёт 1,5 на ядро. Меньше 0,7 на ядро обычно спокойно (запас на всплески: это практическое правило, не закон), около 1 предел, выше 1 перегрузка.
Прикинь сам: load average 4,0. Это перегрузка?
Зависит от ядер. На двух ядрах 4 ÷ 2 = 2 на ядро, это перегрузка вдвое. На шестнадцати 4 ÷ 16 = 0,25, почти пусто. Цифра без числа ядер ничего не значит.
Поиграй с виджетом: процессы то приходят, то уходят. Подвигай «ядер» и «процессов».
Красные кружки это процессы без ядра, жёлтая черта число ядер, толстая линия load average. Смотри, как медленно она догоняет изменения: поэтому чисел три. На 8-ядерной машине тихо, и вдруг запускают 12 неостанавливающихся процессов:
Три значения говорят, когда началась нагрузка. Высоки все три: сервис перегружен давно. Высоко только первое: нагрузка пришла недавно, возможно всплеск.
Осторожно: load average не процент, он легко бывает 50. И в нём считаются процессы в D, которые ждут диск. Поэтому высокий load average при свободном процессоре и высоком wa означает проблему диска, а не процессора.
Проверь понимание:
uptimeна 4-ядерной машине показалload average: 1.20, 3.80, 6.10. Что происходит с нагрузкой?
Ответ
Нагрузка спадает: 15 минут назад было 6,1 на 4 ядра (перегрузка), пять минут назад 3,8 (почти предел), сейчас 1,2 (спокойно). Пик прошёл, причину замедлений ищи в логах за эти 15 минут.
Главное: load average это очередь на процессор (и на диск), и читать его надо на ядро: делим на
nproc, около 1 это предел.
С процессором ясно. Остаётся память, где «свободно» почти ничего, и это нормально. Почему?
Память: что такое available и почему free обманчиво мал
Ты запускаешь free и видишь, что из 16 ГБ свободно 200 МБ. Паника? Нет.
На рабочем столе лежат документы, с которыми ты работаешь, и это память программ. Остальное место занимают бумаги, которые ты недавно брал и держишь под рукой: кэш диска (в выводе buff/cache). Так система не читает недавние файлы заново. Понадобится место, и старые бумаги уберут в шкаф: оно по сути свободно.
Linux занимает всю пустую память под кэш, поэтому free почти всегда мал. Настоящий показатель называется available: сколько можно отдать программам, считая пустую память и кэш, который освободится по первому требованию.
free -h
total used free shared buff/cache available
Mem: 15Gi 3.1Gi 9.2Gi 412Mi 3.3Gi 11Gi
Swap: 4.0Gi 0B 4.0Gi
Ключ -h печатает размеры понятно (Gi это гигабайты, Mi мегабайты). Всего 15 ГБ, программы заняли 3,1 (used), пусто 9,2 (free), кэш 3,3, а доступно 11 (available). Про Swap в следующем разделе. Теперь та же машина, когда один процесс съел почти всё:
total used free shared buff/cache available
Mem: 15Gi 14Gi 182Mi 412Mi 741Mi 412Mi
Swap: 4.0Gi 2.7Gi 1.3Gi
available упал до 412 МБ, кэш почти исчез (его отдали программам). Тревожит именно available: беда начинается, когда его меньше десяти процентов от всей памяти (практическое правило: с меньшим запасом первый всплеск уже загонит систему в swap).
Прикинь сам:
free -hпоказываетfree 300Mi,buff/cache 9Gi,available 9.5Gi. Памяти не хватает?
Ответ
Хватает: available 9,5 ГБ, столько можно отдать программам, освободив кэш. Тревогой стали бы малый available и рост swap.
Что делает система, когда available кончается совсем?
Главное: смотри на
available, а не наfree: Linux держит пустую память под кэш диска, и маленькийfreeэто норма.
Когда память кончилась: swap, OOM killer и утечки
Нехватка памяти страшнее нехватки процессора: сервис не замедляется, а пропадает.
Сначала система спасается диском. Swap (раздел или файл подкачки) это место, куда она убирает редко нужные куски памяти (страницы по 4 КБ). Диск в сотни раз медленнее RAM, поэтому активный swap это беда: процессор простаивает, а всё тормозит. Признак виден в vmstat: колонки si и so (swap in и swap out) перестают быть нулями.
Если не помогло, срабатывает OOM killer (out of memory killer): ядро системы выбирает жертву (обычно процесс, который занял больше всего памяти) и убивает её сигналом SIGKILL. Сервис «падает» без ошибок в своём логе: ему не дали ничего записать. След остаётся в сообщениях ядра:
sudo dmesg -T | grep -i "killed process"
[Sat Oct 3 11:02:44 2026] Out of memory: Killed process 51877 (python3) total-vm:6291456kB, anon-rss:5873212kB
dmesg печатает сообщения ядра, -T делает время читаемым, sudo нужен для доступа. Ядро убило python3 с номером 51877, державший около 5,6 ГБ (anon-rss, его реальная память). Если сервис «пропал» во время теста, а в его логе пусто, проверь это первым делом. А как найти едока, пока он жив?
ps aux --sort=-rss | head -n 4
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
student 52310 12.0 87.5 15728640 14221312 pts/1 S 10:55 2:03 python3 leak.py
postgres 1432 0.8 3.1 1840232 504880 ? Ss Oct01 4:18 postgres: shop shop
--sort=-rss ставит сверху самых прожорливых по реальной памяти. RSS в килобайтах: 14 221 312 КБ это примерно 13,6 ГБ (меньше VSZ, как и должно быть), виновник python3 leak.py. В top то же даёт клавиша M.
Бывает, что процесс растёт понемногу. Это утечка памяти (memory leak): программа берёт память и не отдаёт, и после остановки нагрузки RSS не падает. «Магазин» так себя ведёт при LEAK_ENABLED=1: см. урок 11.4.
Осторожно: если сервис живёт в контейнере, free и top внутри него показывают память всего компьютера, а не лимит контейнера: об этом урок 5.4.
Главное: сначала swap (медленно), потом OOM killer убивает процесс без записи в его логе. Следы ищи в
dmesg.
Осталось то, где всё хранится.
Диск: сколько места и как быстро он работает
Диск ломается двумя независимыми способами: в кладовке нет места, или кладовщик не успевает приносить продукты.
Начнём с места. df -h (disk free) показывает занятость каждого раздела, du -sh каталог (disk usage) считает, сколько занимает один каталог: -s итог, -h понятные единицы.
df -h /
du -h --max-depth=1 ~/perf-lab | sort -h | tail -n 3
Filesystem Size Used Avail Use% Mounted on
/dev/nvme0n1p2 468G 121G 324G 28% /
4.0K /home/student/perf-lab/scripts
12K /home/student/perf-lab/01-linux
48K /home/student/perf-lab
Корень занят на 28%, свободно 324 ГБ. Когда Use% подбирается к 100%, не пишутся логи и падают базы. Вторая показывает самые большие подкаталоги (sort -h правильно сортирует 4.0K, 12M, 3.1G).
Теперь скорость. Диск не успевает, когда к нему стоит очередь запросов. Её показывает iostat -x 1 из sysstat: расширенная статистика (-x) каждую секунду.
avg-cpu: %user %nice %system %iowait %steal %idle
1.8 0.0 9.4 21.3 0.0 67.5
Device r/s w/s rkB/s wkB/s r_await w_await aqu-sz %util
nvme0n1 0.00 3720.00 0.00 951200.00 0.00 0.52 1.93 92.40
Здесь идёт 3720 записей в секунду (w/s), около 930 МБ в секунду (wkB/s). Запрос ждёт в среднем полмиллисекунды (w_await), в очереди почти два запроса (aqu-sz). %util это доля времени, когда диск был занят: 92,4%. У обычного жёсткого диска около 100% значит насыщение. Быстрые SSD и NVMe (диски без движущихся частей) обслуживают много запросов сразу, и 100% у них само по себе не беда. Поэтому смотри на await и aqu-sz: растут, значит диск не справляется. Вверху %iowait 21,3%: столько времени процессор ждал диск.
Почти всё сразу показывает vmstat 1 (virtual memory statistics), строка в секунду.
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
r b swpd free buff cache si so bi bo in cs us sy id wa st
12 0 0 9638492 241512 3690112 0 0 0 12 4310 1980 99 1 0 0 0
2 3 0 9120004 241512 4100008 0 0 0 951200 1810 920 2 9 67 21 0
Настоящую первую строку vmstat пропускают (это среднее с момента включения), в примере её нет. Здесь первая строка: в очереди на процессор 12 процессов (r), us 99. Вторая: диск пишет (bo), три процесса ждут его (b), wa 21%.
Прикинь сам: load average 6 на 8 ядрах, процессор простаивает на 70%,
waравен 25%. Где искать проблему?
Ответ
В диске. Процессор простаивает, но wa 25% показывает, что процессы ждут ввода-вывода, а они в состоянии D входят в load average. Смотри iostat -x 1 (%util, await) и ищи процесс, который много пишет или читает.
Осторожно: df молчит о скорости, а iostat о месте.
Место кончается и по числу файлов (inodes, записей о файлах): миллион крошечных файлов забьёт лимит при свободных гигабайтах, покажет df -i.
Главное: у диска две независимые беды: место (
df -h) и скорость (iostat -x 1,vmstat). Смотри очередь иawait, а не только%util.
Как не метаться между четырьмя признаками, когда всё тормозит?
Метод USE: что проверять про каждый ресурс
Когда всё тормозит, хочется смотреть всё сразу, и это путь к растерянности. Опытные люди задают про каждый ресурс три вопроса, это метод USE: Utilization, Saturation, Errors (занятость, очередь, ошибки). Занят ли ресурс и на сколько процентов, есть ли очередь, были ли сбои.
У процессора занятость это us + sy, очередь это load average против ядер и колонка r. У памяти занятость видна в available, очередь это si и so, ошибка это OOM killer в dmesg. У диска занятость это %util, очередь это aqu-sz, await и wa. У места очереди нет, а ошибка звучит в логах No space left on device. Вот схема из начала, теперь с командами:
flowchart TD
A["Сервис тормозит"] --> B{"load average выше<br/>числа ядер?<br/>(uptime, nproc)"}
B -->|да| C{"us + sy высокие?<br/>(top)"}
B -->|нет| F{"available почти ноль,<br/>swap растёт?<br/>(free -h, vmstat)"}
C -->|да| D["Процессор: ищем процесс<br/>(top, ps)"]
C -->|нет, wa высокий| E["Диск: iostat -x 1"]
F -->|да| G["Память: ps --sort=-rss"]
F -->|нет| H["Снаружи: сеть, база"]
Осторожно: «занят на 100%» и «есть очередь» это разные вещи. Ресурс на 100% без очереди работает на полную мощность, а с очередью начинает ограничивать систему. На 8 ядрах процессор занят на 30%, но load average 12: десяток процессов ждёт диск, а загрузка процессора их не видит. Очередь надёжнее занятости.
Главное: про каждый ресурс спрашивай три вещи: занят ли, есть ли очередь, были ли ошибки. Очередь важнее занятости.
Наблюдение во время теста
Во время теста разовый снимок почти бесполезен: нагрузка нарастает, и важна динамика. Простейший вариант: писать вывод в файл параллельно с тестом.
vmstat 1 > ~/perf-lab/results/vmstat-test1.txt &
> отправляет вывод в файл, & уводит команду в фон. После теста её останавливают kill %1 или pkill vmstat, а файл остаётся: его разбирают awk, например считают средний us. Для одного процесса есть pidstat -u -r -p PID 1 (тоже из sysstat): процессор (-u) и память (-r) каждую секунду. В теме 7 то же станет автоматическим, с графиками в Grafana.
Вернёмся к отчёту «ответы стали медленными, на 200 пользователях упали». Теперь у тебя есть порядок: очередь процессов, память, диск, и запись цифр рядом с тестом. Коллеге из моей истории хватило трёх команд.
Главное: во время теста пиши
vmstat 1иiostat -x 1в файл, а не смотри разово: по записи видно, когда и что упёрлось.
Практика
Все упражнения безопасны: ты нагружаешь только свою машину и всё останавливаешь после. Если у тебя другое число ядер, подставляй своё значение вместо моего (в примерах 8 ядер и 15 ГБ).
1. Поставь наблюдателей и сними состояние покоя
sudo apt update
sudo apt install -y sysstat
mkdir -p ~/perf-lab/01-linux
cd ~/perf-lab/01-linux
uptime
nproc
free -h
df -h /
vmstat 1 3
10:32:18 up 3 days, 2:14, 1 user, load average: 0.42, 0.51, 0.48
8
total used free shared buff/cache available
Mem: 15Gi 3.1Gi 9.2Gi 412Mi 3.3Gi 11Gi
Swap: 4.0Gi 0B 4.0Gi
Filesystem Size Used Avail Use% Mounted on
/dev/nvme0n1p2 468G 121G 324G 28% /
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
r b swpd free buff cache si so bi bo in cs us sy id wa st
1 0 0 9638492 241512 3690112 0 0 12 14 410 980 3 1 96 0 0
0 0 0 9638240 241512 3690112 0 0 0 0 280 610 1 0 99 0 0
0 0 0 9638240 241512 3690112 0 0 0 44 305 655 1 1 98 0 0
Как читать вывод: это твоя «норма». Запиши числа: load average около 0,4 на 8 ядрах (почти пусто), available 11 ГБ, swap не тронут, id около 98%. Когда машина будет нагружена, ты будешь сравнивать с этим. Первая строка vmstat это среднее с момента включения, смотри строки после неё.
Типичные ошибки:
E: Unable to locate package sysstat: не выполнилsudo apt update.iostat: command not foundпосле установки: ещё не ставилиsysstatили оболочка не обновила кэш команд:hash -rили открой новый терминал.
2. Нагрузи процессор: load average против ядер
Открой два терминала (как открыть второе окно, написано в уроке 1.1; в Multipass второе окно это новый multipass shell lab). В первом запусти нагрузку:
for i in {1..4}; do yes > /dev/null & done
jobs
[1] Running yes > /dev/null &
[2] Running yes > /dev/null &
[3]- Running yes > /dev/null &
[4]+ Running yes > /dev/null &
for ... do ... done цикл, который четыре раза запускает yes в фоне (циклы подробно в уроке 1.5): {1..4} разворачивается в 1 2 3 4. Во втором терминале посмотри:
top
Внутри top нажми 1 (покажет каждое ядро отдельно), потом q для выхода. Типичная картина на 8 ядрах:
top - 10:43:51 up 3 days, 2:25, 2 users, load average: 2.84, 1.02, 0.45
Tasks: 318 total, 5 running, 313 sleeping, 0 stopped, 0 zombie
%Cpu(s): 50.1 us, 0.3 sy, 0.0 ni, 49.6 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
48301 student 20 0 8344 640 576 R 99.7 0.0 0:32.10 yes
48302 student 20 0 8344 640 576 R 99.7 0.0 0:32.07 yes
48303 student 20 0 8344 640 576 R 99.3 0.0 0:32.03 yes
48304 student 20 0 8344 640 576 R 99.3 0.0 0:32.01 yes
Как читать вывод: четыре процесса заняли четыре ядра из восьми, поэтому общая загрузка около 50%. Load average растёт плавно: первое число уже 2,84, пятиминутное 1,02, к четырём первое число придёт за пару минут. Очереди нет: четыре процесса на восемь ядер.
Теперь добавь нагрузку выше числа ядер. Всего должно работать примерно в полтора раза больше процессов, чем ядер: на 8 ядрах это 12, то есть к четырём запущенным добавь ещё восемь. На другом числе ядер считай так: всего должно быть nproc * 1,5 процессов, значит добавь это число минус 4 (при 4 ядрах всего 6, добавь 2; при 2 ядрах хватит 3 всего, этот шаг пропусти). В первом терминале (замени 8 на своё число):
for i in {1..8}; do yes > /dev/null & done
Теперь работают 12 процессов. Подожди минуту и во втором терминале выполни:
uptime
vmstat 1 4
10:46:09 up 3 days, 2:27, 2 users, load average: 9.31, 4.78, 2.04
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
r b swpd free buff cache si so bi bo in cs us sy id wa st
12 0 0 9631204 241512 3690444 0 0 0 0 4021 1820 99 1 0 0 0
13 0 0 9631204 241512 3690444 0 0 0 0 4106 1913 99 1 0 0 0
12 0 0 9631204 241512 3690444 0 0 0 12 4088 1894 99 1 0 0 0
Как читать вывод: r равно 12: в очереди на процессор 12 процессов, а ядер 8, значит четыре ждут. Загрузка 99% us, простоя нет, а load average ползёт к 12. Это и есть «упёрлись в процессор»: ядра заняты, и есть очередь. Если бы это был сервис, его запросы теперь ждали бы свою очередь на процессор, и время ответа росло бы.
Типичные ошибки: -bash: fork: retry: Resource temporarily unavailable: ты случайно запустил слишком много процессов. Выполни pkill yes и начни с меньшего числа.
Останови нагрузку (в любом терминале, pkill работает на уровне всей системы):
pkill yes
jobs
uptime
pkill yes послал SIGTERM всем процессам с именем yes. uptime сразу после показывает, что load average ещё высокий: первое число падает за минуту-две, пятнадцатиминутное остаётся высоким дольше. Эту инерцию учитывай, когда меряешь uptime сразу после теста.
3. Займи память
python3 -c "import time; x = b'x' * 2_000_000_000; time.sleep(120)" &
sleep 2
free -h
ps -o pid,rss,vsz,cmd -p $!
[1] 49102
total used free shared buff/cache available
Mem: 15Gi 5.0Gi 7.3Gi 412Mi 3.3Gi 9.2Gi
Swap: 4.0Gi 0B 4.0Gi
PID RSS VSZ CMD
49102 1957548 1969092 python3 -c import time; x = b'x' * 2_000_000_000; time.sleep(120)
Коротко про команду: Python создаёт строку из двух миллиардов букв x (около 1,9 ГБ), и две минуты держит её (time.sleep(120)). $! оболочка заменяет на PID последнего запущенного в фоне процесса. sleep 2 даёт процессу время выделить память.
Как читать вывод: used вырос примерно на 1,9 ГБ (с 3,1 до 5,0), available упал с 11 до 9,2 ГБ. RSS процесса около 1,9 ГБ (1 957 548 КБ): память реально занята. Теперь найди «виновника» как это делают на работе, не зная PID:
ps aux --sort=-rss | head -n 3
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
student 49102 0.0 12.3 1969092 1957548 pts/0 S 10:52 0:01 python3 -c import time; x = ...
student 2210 1.1 3.9 4890124 622440 ? Sl Oct01 8:42 /usr/bin/gnome-shell
Первым стоит наш python3: он на первом месте по RSS. Остановка: kill %1 (или kill 49102), и free -h покажет, что память вернулась.
Типичные ошибки: MemoryError: на машине меньше свободной памяти, чем ты просишь. Уменьши число (1_000_000_000).
4. Нагрузи диск и найди его в iostat
Нужно 5 ГБ свободного места. В первом терминале запусти iostat, во втором запись:
# первый терминал
iostat -x 1
# второй терминал
dd if=/dev/zero of=~/perf-lab/01-linux/big.bin bs=1M count=5120 conv=fdatasync status=progress
dd копирует данные «из» (if, input file) «в» (of, output file). /dev/zero специальный файл, из которого бесконечно читаются нули, bs=1M размер блока 1 МБ, count=5120 число блоков (5 ГБ), conv=fdatasync дожидаться, пока данные реально лягут на диск, а не в кэш памяти. status=progress показывает ход работы.
5120+0 records in
5120+0 records out
5368709120 bytes (5.4 GB, 5.0 GiB) copied, 6.1 s, 880 MB/s
А в iostat во время записи:
avg-cpu: %user %nice %system %iowait %steal %idle
1.8 0.0 9.4 21.3 0.0 67.5
Device r/s w/s rkB/s wkB/s r_await w_await aqu-sz %util
nvme0n1 0.00 3720.00 0.00 951200.00 0.00 0.52 1.93 92.40
Как читать вывод: диск писал около 880 МБ/с, %util достиг 92%: он занят почти постоянно. %iowait поднялся до 21%: часть процессорного времени потрачена на ожидание диска. Когда dd завершился, %util вернулся в ноль. Твои цифры будут другими: у SSD, обычного жёсткого диска и виртуальной машины скорости различаются в десятки раз. Запомни своё значение: это максимум скорости записи твоего диска.
Убери за собой и посмотри, что занимает место:
rm ~/perf-lab/01-linux/big.bin
du -h --max-depth=1 ~ | sort -h | tail -n 4
df -h ~
Типичные ошибки: dd: failed to open ... Permission denied ты пишешь не в свой каталог; No space left on device слишком большой count. Забыл rm: 5 ГБ файла останутся навсегда.
5. Запиши шпаргалку
cd ~/perf-lab/01-linux
cat > resource-notes.md <<'EOF'
# Шпаргалка: что смотреть на машине
Моя машина: ядер ___, память ___, диск (скорость записи dd) ___ МБ/с.
| Вопрос | Команда | Норма | Перегрузка |
|---|---|---|---|
| Сколько процессов ждут процессор | uptime, vmstat 1 (колонка r) | load average < ядер | load average > ядер, r > ядер |
| Сколько памяти реально свободно | free -h (колонка available) | available > 10% | available почти 0, swap растёт (si, so) |
| Кто занял память | ps aux --sort=-rss, потом head | обычные сервисы | один процесс с огромным RSS |
| Кто занял процессор | top, ps aux --sort=-%cpu, потом head | нет лидера | один процесс 100% и больше |
| Диск не успевает | iostat -x 1 (%util, await), vmstat (wa) | %util < 60% | %util около 100%, wa высокий |
| Диск заполнен | df -h | Use% < 80% | Use% около 100% |
EOF
nano resource-notes.md
Открой файл в nano и допиши свои числа: ядра, память, скорость записи диска из прошлого упражнения. Этой шпаргалкой будешь пользоваться на стенде.
Цифры в
topне сходятся с твоей гипотезой? Сначала проверь по алгоритму из урока (load average против ядер,wa,available), потом спроси нейросеть. Ответ проверь второй командой, напримерvmstat 1илиiostat -x 1.
Сломай и почини
Поломка. Машина «тормозит»: запусти в первом терминале
for i in {1..12}; do yes > /dev/null & done
(на машине с другим числом ядер возьми число в полтора раза больше ядер.) Перейди во второе окно и сделай вид, что не знаешь, что запустил.
Задача. Во втором терминале, используя только команды из урока, выясни: есть ли перегрузка, чем она вызвана, какой процесс виноват, и останови его. Сравни с «нормой» из упражнения 1.
Разбор
uptimeиnproc: load average (например,9.8, 4.1, 1.5) выше числа ядер 8, и растёт (первое число больше третьего). Перегрузка есть, и она свежая.top: в строке%Cpu(s)usоколо 99,waноль,idноль. Значит, ядра заняты работой, а не ждут диск: перегрузка по процессору.free -hпокажетavailableбез изменений: память ни при чём.- В таблице
top(илиps aux --sort=-%cpu | head) двенадцать строкyes, у каждого около 66% CPU. Виновник найден: программаyes. - Останови:
pkill yes(илиkill PIDпо очереди). Еслиyesзапущен из другого терминала, фоновые задания чужого терминала черезjobsне видны, ноpkillиkillработают на уровне системы и найдут их. - Проверь:
uptime(первое число начнёт падать, пятнадцатиминутное ещё долго остаётся высоким) иtop(процессовyesбольше нет).
Вывод: «упёрлись в процессор» диагностируется двумя признаками одновременно: load average выше числа ядер и us + sy около 100%. Один признак без другого вводит в заблуждение: при высоком load average и высоком wa проблема в диске, а не в процессоре.
ИИ в помощь
Нейросеть быстро объясняет выводы top, free и iostat, но не знает состояния твоей машины: нужны цифры из твоего вывода. Общие правила на странице ИИ-помощник.
Задача: прочитать вывод top или free -h.
Linux Ubuntu 24.04, <число ядер> ядер, <объём> ГБ памяти. Вывод top в момент замедления:
<вставь первые 15 строк top>
Объясни каждое поле шапки (load average, us, sy, wa, MiB Mem, avail Mem). Это упёрлось в процессор, память или диск? Какой вывод какой команды мне нужен дальше, чтобы подтвердить?
Проверь ответ: сверь вывод со своими числами: load average против числа ядер (nproc), available в free -h. Типичная ошибка: нейросеть называет занятую память проблемой, хотя Linux держит в ней кэш, и путает free с available.
Задача: найти процесс-виновника и безопасно его остановить.
Ubuntu 24.04. `ps aux --sort=-%cpu | head` показывает:
<вставь вывод>
Какой процесс подозрителен и почему? Как корректно остановить: сначала `kill` с обычным сигналом, и когда допустим `kill -9`? Что может потеряться?
Проверь ответ: перед kill проверь PID и имя командой ps -p <PID> -o pid,cmd. Типичная ошибка: совет сразу kill -9 или остановка системных процессов: убивай только то, что запустил сам в практике.
Словарик урока
| Термин | Простыми словами |
|---|---|
| Процесс (process) | Запущенная программа: у неё свой номер, владелец и состояние |
| PID | Номер процесса, по нему к нему обращаются |
| Сигнал (signal) | Короткое сообщение процессу: SIGTERM «заверши», SIGKILL «убит без вопросов» |
Состояния R, S, D, Z |
Работает или ждёт ядро; спит; ждёт диск и не слышит сигналов; зомби |
| Ядро процессора (core) | Один «повар»: в каждый момент выполняет один процесс |
us, sy, id, wa, st |
Куда ушло время ядра: программы, система, простой, ожидание диска, чужая виртуалка |
| Load average | Среднее число процессов, которые работают или ждут процессор или диск; за 1, 5 и 15 минут |
| Память (RAM) | Быстрое хранилище того, с чем программа работает прямо сейчас |
available |
Сколько памяти можно отдать программам, включая освобождаемый кэш |
| Кэш диска (buff/cache) | Память, занятая недавно прочитанными файлами; освобождается по требованию |
| Swap | Место на диске, куда система убирает память, когда RAM не хватает; медленный |
| RSS | Сколько оперативной памяти процесс занимает прямо сейчас |
| OOM killer | Механизм ядра, убивающий процесс, когда память кончилась совсем |
| Утечка памяти | Программа берёт память и не отдаёт: расход растёт с каждым запросом |
%util, await |
Занятость диска и сколько мс ждёт один запрос к диску |
| Узкое место (bottleneck) | Ресурс, который кончился первым и ограничивает работу всей системы |
| Метод USE | Для каждого ресурса проверь: занятость, очередь, ошибки |
Вопросы с собеседований
Раздел для повторения: ответь вслух, потом открой ответ. Вопросы с пометкой [на скорость] тренируй на время: ответ за 30 секунд.
1. [junior] [часто] Что такое load average и как его читать?
Ответ
Среднее число процессов, которые работают или готовы работать и ждут процессор (в Linux ещё и те, что ждут диск), за последние 1, 5 и 15 минут. Читают его относительно числа ядер: load average 8 на 8 ядрах это предел, на 4 ядрах двойная перегрузка. Если первое число больше третьего, нагрузка растёт, если меньше, спадает.
Что хотят услышать: три окна, сравнение с числом ядер, направление тренда.
Красный флаг: «это процент загрузки процессора».
2. [junior] [часто] Чем free отличается от available в выводе free -h?
Ответ
free это честно пустая память. available это сколько можно отдать программам: пустая плюс то, что занято кэшем диска и освобождается по первому требованию. Linux специально использует свободную память под кэш, поэтому free почти всегда мал. Смотреть нужно available.
Что хотят услышать: кэш диска, что free мал в норме.
Красный флаг: «памяти почти нет, потому что free 200 МБ».
3. [junior] [часто] Как найти процесс, который занимает больше всего памяти или процессора?
Ответ
top (сортировка по процессору по умолчанию, M по памяти) или ps aux --sort=-%cpu | head и ps aux --sort=-rss | head. По памяти смотрят RSS, не VSZ.
Что хотят услышать: оба инструмента, RSS как реальный расход.
Красный флаг: путает RSS с VSZ.
4. [junior] Чем kill отличается от kill -9?
Ответ
kill посылает SIGTERM (15): просьбу завершиться, процесс может сохранить данные, закрыть файлы и соединения. kill -9 посылает SIGKILL: процесс убирается ядром немедленно, ничего не успев сделать. Сначала всегда обычный kill, -9 когда процесс не реагирует.
Что хотят услышать: «просьба» против «принудительно», порядок.
Красный флаг: «сразу kill -9, чтобы наверняка».
5. [junior] Что значит состояние процесса D и чем оно опасно?
Ответ
D (uninterruptible sleep): процесс ждёт ввода-вывода (диск, сетевая файловая система) и не реагирует на сигналы, даже на kill -9. Много процессов в D и высокий wa указывают на проблему с диском. Такие процессы входят в load average.
Что хотят услышать: связь с диском, не убивается, влияет на load average.
Красный флаг: не знает состояний.
6. [junior] Что такое swap и когда он плох?
Ответ
Swap это место на диске, куда система убирает редко используемые страницы памяти, когда RAM не хватает. Диск в сотни раз медленнее, поэтому если swap активно используется (si и so в vmstat не нули), система тормозит: это признак нехватки памяти.
Что хотят услышать: диск вместо RAM, si/so как признак.
Красный флаг: «swap это тоже оперативная память».
7. [junior] Что такое OOM killer и как понять, что он сработал?
Ответ
Механизм ядра, который убивает процесс, когда память закончилась и swap не помог. Со стороны это выглядит как внезапное падение сервиса без ошибки в его логе. Следы в логе ядра: sudo dmesg -T | grep -i "killed process" или journalctl -k.
Что хотят услышать: где искать, что в логе сервиса пусто.
Красный флаг: «посмотрю логи приложения» и всё.
8. [junior] Как понять, что диск не успевает?
Ответ
iostat -x 1: %util близко к 100% и растущие await (миллисекунды ожидания) и aqu-sz (очередь). Косвенные признаки: wa в top и vmstat, процессы в D, высокий load average при свободном процессоре.
Что хотят услышать: iostat, %util и await, косвенные признаки.
Красный флаг: смотрит только df -h (это место, не скорость).
9. [middle] Load average 20 на 8-ядерной машине, процессор занят на 30%. Что это может быть и как проверить?
Ответ
Много процессов в состоянии D: они ждут диск или сетевой диск и входят в load average, не нагружая процессор. Проверю vmstat 1 (колонки b и wa), iostat -x 1 (%util, await), ps aux | awk '$8 ~ /D/' для процессов в состоянии D. Если wa высокий, виноват диск или хранилище, а не процессор.
Что хотят услышать: D входит в load average, идти в диск, не в процессор.
Красный флаг: «добавить ядер».
10. [middle] Сервис тормозит под нагрузкой. Как по шагам понять, какой ресурс упёрся?
Ответ
Для каждого ресурса проверяю занятость, очередь и ошибки (метод USE). Процессор: top и load average против числа ядер, r в vmstat. Память: available в free -h, si/so, OOM в dmesg. Диск: iostat -x (%util, await), wa. Место: df -h. Если все четыре в порядке, смотрю наружу: сеть и зависимости (база, внешние сервисы). Затем нахожу процесс-виновник в top или ps.
Что хотят услышать: систематичность, очередь отдельно от занятости, переход к процессу.
Красный флаг: «посмотрю top» без дальнейшего плана.
11. [middle] У процесса RSS растёт со временем и не падает после остановки нагрузки. Что это значит?
Ответ
Похоже на утечку памяти: процесс берёт память и не возвращает её. Подтверждение: ряд значений RSS во времени монотонно растёт (ps -o rss -p PID раз в минуту или pidstat -r). Без вмешательства закончится OOM killer или swap. Нужно проверить, не рост ли это легитимного кэша приложения, и дальше искать утечку в коде.
Что хотят услышать: динамика, не разовое значение; не путать с кэшем; чем закончится.
Красный флаг: «просто перезапущу».
12. [junior] [на скорость] Как узнать число ядер процессора?
Ответ
nproc. Подробнее lscpu.
Что хотят услышать: nproc.
Красный флаг: «в диспетчере задач».
13. [junior] [на скорость] Какой load average считается предельным для 4-ядерной машины?
Ответ
Около 4: по одному процессу на ядро, очереди нет. Выше значит, что процессы ждут.
Что хотят услышать: «равно числу ядер».
Красный флаг: называет число без привязки к ядрам.
14. [junior] [на скорость] Какая команда покажет, сколько места на дисках?
Ответ
df -h. А сколько занимает каталог: du -sh каталог.
Что хотят услышать: обе команды и -h.
Красный флаг: путает df и du.
15. [junior] [на скорость] Как запустить команду в фоне и вернуть её на передний план?
Ответ
команда & в фон, jobs посмотреть список, fg вернуть на передний план. Ctrl+Z приостановит передний процесс, bg продолжит его в фоне.
Что хотят услышать: &, jobs, fg.
Красный флаг: не знает &.
Проверено на версиях
Ubuntu 24.04.3 LTS и 26.04 LTS: procps-ng (top, ps, free, vmstat), sysstat 12.x (iostat, pidstat), coreutils 9.4 (dd, df, du), Python 3.12. Колонки iostat -x зависят от версии sysstat: названия r_await, w_await, aqu-sz, %util одинаковы в версиях 12.x. Октябрь 2026.
Итог урока: ты умеешь
- Объяснить, что такое процесс, PID, состояния
R,S,D,Z, и остановить процесс сигналом (kill,pkill). - Прочитать
top: строку%Cpu(s), память, таблицу процессов, отличитьusотwa. - Прочитать load average и сравнить его с числом ядер: есть очередь или нет.
- Отличить
freeотavailable, найти процесс поRSSи признак OOM killer вdmesg. - Проверить место (
df -h,du) и скорость диска (iostat -x,vmstat). - Для каждого ресурса проверить занятость, очередь и ошибки (метод USE).
- Сохранить наблюдение за машиной во время теста в файл (
vmstat 1 > файл &). - Определить узкое место по комбинации показателей и найти виновный процесс.
Дальше: урок 1.4. Сеть из терминала: последний ресурс, сеть, и как отличить проблему сети от ошибки HTTP.
Проверь себя
Короткий тест по уроку: 5 вопросов из банка в 30. Засчитывается только полностью правильный ответ, порог 60%. Каждая новая попытка даёт другие вопросы, пока банк не закончится. Ответы видны после проверки.
Тест работает с включённым JavaScript.