Загрузка CPU: user, system, iowait, steal и контекст-свитчи

Рейтинг: 40.9% · 8 голосов
Подробный курс по диагностике и производительности Linux для админов и DevOps: с чего начать, методология USE, логи (journalctl, dmesg), процессы, CPU, память и OOM, диск и IO (iostat), сеть (ss, tcpdump), системные вызовы и strace, ltrace, perf, флеймграфы, ftrace, eBPF и bpftrace, lsof, мониторинг. Разбор каждого инструмента с чтением вывода.
Ответить
Аватара пользователя
Dmitry_SRE
Сообщения: 47
Зарегистрирован: 11 май 2026, 05:31

Загрузка CPU: user, system, iowait, steal и контекст-свитчи

Сообщение Dmitry_SRE »

Оглавление курса (47)
  1. С чего начать диагностику Linux: методология вместо паники
  2. Первые 60 секунд: экспресс-диагностика нагруженного сервера
  3. Методологии диагностики: USE, RED и здравый смысл
  4. Логи systemd через journalctl: где искать причину
  5. Где лежат логи Linux: /var/log, dmesg и rsyslog
  6. Источники правды: load average, /proc и /sys
  7. Процессы Linux: ps, pstree и состояния процессов
  8. Интерактивный мониторинг: top и htop
  9. Метрики процессов во времени: pidstat
  10. Приоритеты и ограничения: nice, ionice, cgroups
  11. Сигналы и зависшие процессы: kill, и что делать с D-state
  12. Загрузка CPU: user, system, iowait, steal и контекст-свитчи (вы здесь)
  13. Диагностика CPU по ядрам: vmstat и mpstat
  14. Профилирование CPU: perf top и поиск пожирателя
  15. Частоты, троттлинг и NUMA: turbostat и numastat
  16. Память Linux: RSS, VSZ, page cache и миф о нехватке памяти
  17. Сколько памяти занято: free, /proc/meminfo и vmstat
  18. Поиск утечек и пожирателей памяти: smem, pmap, smaps
  19. Swap и подкачка: swapon, swappiness, когда своп - это боль
  20. OOM killer: кто и за что убил процесс
  21. Память ядра и slab: slabtop и куда уходит RAM
  22. Подсистема ввода-вывода: путь запроса от приложения до диска
  23. Диагностика диска: iostat и чтение await, %util
  24. Кто грузит диск: iotop и атрибуция io процессам
  25. Файловые системы: df, du, иноды и куда делось место
  26. Глубокая диагностика блочного слоя: blktrace и biolatency
  27. Кеш страниц и грязные данные: dirty pages, fsync, drop_caches
  28. Сетевой стек Linux: путь пакета и где возникают задержки
  29. Сокеты и соединения: ss и netstat
  30. Захват трафика: tcpdump для диагностики сети
  31. Задержки и потери в сети: ping, mtr, диагностика латентности
  32. Сеть как источник проблем: nftables, conntrack, дропы
  33. Что такое системный вызов и зачем его трассировать
  34. strace: трассировка системных вызовов на практике
  35. strace в бою: почему программа висит, падает или тормозит
  36. ltrace: трассировка вызовов библиотек
  37. Накладные расходы трассировки и безопасные альтернативы
  38. perf: универсальный профайлер Linux
  39. Флеймграфы: визуализация профиля производительности
  40. Трассировка ядра: ftrace и trace-cmd
  41. eBPF: революция в наблюдаемости Linux
  42. Инструменты eBPF на практике: BCC и bpftrace
  43. lsof: открытые файлы, дескрипторы, кто держит файл и порт
  44. Разбор кейса: приложение тормозит - пошаговая диагностика
  45. Непрерывный мониторинг: sar, node_exporter, Grafana, Zabbix
  46. Диагностика в контейнерах: cgroups, лимиты, Docker и Kubernetes
  47. Карта инструментов и сквозной разбор инцидента производительности
Ты смотришь в мониторинг, видишь "CPU 90%" и сразу думаешь: процессор не справляется, надо добавить ядер. Иногда это правда. А иногда ты добавишь ядра, заплатишь за них, и ничего не изменится - потому что 90% это была не работа процессора, а ожидание диска. Цифра одна, а причины за ней совершенно разные.

Этот урок про то, что на самом деле скрывается за процентами. Когда ты разберёшь загрузку CPU в Linux на режимы - user, system, iowait, steal и остальные - ты перестанешь гадать и начнёшь чинить именно то, что сломано. Это база, без которой strace и perf из следующих глав будут стрельбой вслепую. Сразу договоримся о терминах: "загрузка процессора" - это не одно число, а распределение времени по режимам, и читать его надо именно так.

Из чего складывается нагрузка CPU в Linux

Процессор в каждый момент времени что-то делает или простаивает. Ядро ведёт учёт в тиках (jiffies) и складывает их в счётчики, которые лежат в /proc/stat (строка cpu и строки cpu0, cpu1, ...). Все утилиты - vmstat, mpstat, top - читают именно эти счётчики, берут разницу за интервал и переводят в проценты. Поэтому первое значение любой утилиты, рассчитанное "с момента загрузки", почти бесполезно - смотри на последующие интервалы.

Разберём все режимы - это и есть ключ к диагностике.
  • user (us, %usr) - процессор выполняет код твоих приложений в пространстве пользователя. Тут крутятся циклы, парсинг JSON, сжатие, шифрование, расчёты. Высокий user - программа реально считает.
  • system (sy, %sys) - процессор внутри ядра по запросу приложения. Сюда попадают системные вызовы (открыть файл, отправить пакет, выделить память), работа сетевого стека, файловой системы. Высокий system - приложение дёргает ядро слишком часто.
  • nice (ni, %nice) - тот же user, но для процессов с пониженным приоритетом (запущенных через nice, положительный nice). Считается отдельно, чтобы фоновые задачи было видно.
  • iowait (wa, %iowait) - процессор простаивает и при этом в системе есть хотя бы один незавершённый запрос дискового ввода-вывода. Запомни главное: это НЕ занятость процессора. Это разновидность простоя.
  • idle (id, %idle) - процессор простаивает и ничего не ждёт. Свободен.
  • steal (st, %steal) - ты в виртуалке, и время твоего виртуального процессора "украдено" гипервизором в пользу соседних виртуалок на том же физическом хосте.
  • irq и softirq (hi/si в top, %irq/%soft в mpstat) - обслуживание аппаратных и программных прерываний. Чаще всего softirq растёт от интенсивного сетевого трафика или таймеров.
  • guest (%guest, %gnice) - время, отданное на исполнение гостевых vCPU, если твоя машина сама гипервизор (KVM-хост). На обычном сервере это нули, но в современном sysstat колонки есть, не пугайся.
Сумма всех режимов даёт 100% на ядро. Поэтому первый навык - не смотреть на одну цифру "загрузка процессора", а сразу спрашивать: из каких режимов она состоит.

Изображение

iowait это НЕ загрузка процессора, а простой в ожидании

Это место, где спотыкаются почти все новички, поэтому остановимся отдельно. Представь повара (процессор) и духовку (диск). Повар поставил блюдо в духовку и стоит, ждёт. Кухня выглядит занятой - повар на месте, не уходит. Но повар-то ничего не делает, он простаивает в ожидании духовки. Вот это и есть iowait: процессор свободен, но в системе висит запрос на диск, и ядро отдельно учитывает этот простой как "ждём io".

Тонкость, которую полезно знать: iowait не означает, что именно столько процентов времени диск был узким местом. Это лишь доля idle, во время которого был хоть один незавершённый io-запрос. Если в этот же момент появится готовая к счёту задача, ядро отдаст ей ядро, и iowait упадёт, хотя диск медленнее не стал. Поэтому iowait - это подсказка, а не диагноз: увидел высокий wa - иди подтверждать его на стороне диска (iostat, await, %util из следующих глав).

Практический вывод жёсткий: если у тебя высокий iowait в Linux, добавлять ядра процессора БЕСПОЛЕЗНО. Узкое место - диск (или сеть). Лечится это быстрым NVMe, оптимизацией запросов, кешированием, индексами в базе - но не процессором. А ещё iowait обманчив на многоядерных машинах: если одно ядро из 16 ждёт диск, общий iowait будет около 6%, хотя для этого конкретного приложения диск - стена. Поэтому смотри на iowait в разбивке по ядрам, а не только среднее.

Практика: читаем загрузку CPU по колонкам

Начнём с vmstat - он показывает систему в динамике. Аргумент 1 значит "обновляй раз в секунду".

Код: Выделить всё

vmstat 1
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
 2  0      0 412300  18400 980200    0    0    12    34  210  450 35  8 55  2  0
 1  1      0 410800  18400 981000    0    0  4096     0  900 1500  9 12 20 59  0
Что тут важно. Первая строка - это среднее с момента загрузки, её игнорируй, смотри со второй. Колонки CPU в конце: us (user), sy (system), id (idle), wa (iowait), st (steal) - те самые проценты, в сумме 100. Во второй строке у нас wa=59: процессор больше половины времени тупо ждёт диск, и это подтверждается bi=4096 (блоки читаются с устройства, единица - 1024 байта/с). Это io-bound нагрузка, не CPU-bound.

Слева важнейшие колонки r и b. r - сколько задач прямо сейчас готовы выполняться (бегут на ядре или стоят в очереди на процессор, runnable). b - сколько застряло в непрерываемом сне (состояние D, uninterruptible sleep), почти всегда это ожидание диска или другого медленного драйвера. Колонка in - аппаратные прерывания в секунду, cs - контекст-переключения в секунду, к ним вернёмся.

Теперь mpstat -P ALL - он раскладывает по ядрам. Это лучший инструмент, чтобы поймать, что нагружено не "вообще", а конкретное ядро.

Код: Выделить всё

mpstat -P ALL 1
CPU    %usr   %nice    %sys %iowait    %irq   %soft  %steal  %guest  %gnice   %idle
all   28.50    0.00    9.20    0.50    0.00    1.30   12.00    0.00    0.00   48.50
  0   30.00    0.00    8.00    1.00    0.00    0.50   11.00    0.00    0.00   49.50
  1   95.00    0.00    3.00    0.00    0.00    0.00    2.00    0.00    0.00    0.00
Читаем по полям. Строка all - усреднение по всем ядрам, оно прячет проблему: вроде %idle=48, живём. Но ядро 1 забито на 95% в %usr и %idle=0 - туда воткнулся однопоточный процесс и упёрся в потолок одного ядра. Среднее это размазало. Ещё обрати внимание на %steal=12 в строке all - это тревожный звонок про виртуалку, разберём ниже. А %soft=1.3 - software interrupts, обычно от сети; если он лезет к десяткам процентов на одном ядре, копай сетевой трафик и распределение прерываний по ядрам (RPS/RSS).

Команда top тоже показывает разбивку - строка %Cpu(s) вверху: us, sy, ni, id, wa, hi (hardware irq), si (softirq), st. Это те же режимы, просто короткими именами. По нажатию клавиши 1 в top строка разворачивается по ядрам, а в btop (популярная замена top в 2026) ядра видны графиками сразу.

Чтобы найти виновный процесс, а не только режим, бери pidstat из того же пакета sysstat:

Код: Выделить всё

pidstat -u 1
07:12:30      UID       PID    %usr %system  %guest   %wait    %CPU   CPU  Command
07:12:31     1000      8123   96.00    2.00    0.00    0.00   98.00     1  ffmpeg
Колонка %CPU - суммарная загрузка процессом (может быть больше 100 на многопоточном), %usr и %system - разбивка как у системы, %wait - сколько процесс сам ждал очередь на CPU. Высокий %wait у процесса при свободных ядрах = планировщик не успевает его пускать, дальше идём к run queue.

steal cpu, run queue и контекст-переключения

steal. Если ты на VPS или в облаке и видишь %steal стабильно выше 5-10%, это значит, что физический хост перепродан (oversubscribed): на одном железе слишком много виртуалок, и гипервизор не успевает выдавать тебе процессорное время. Твоё приложение тормозит, но это не его вина и не вина твоего кода - тебя обделяет хост провайдера. Лечение: писать в саппорт, мигрировать на другой хост или тариф. Внутри виртуалки steal не убрать. На голом железе steal всегда 0, его там просто нет. Важная оговорка 2026: в контейнерах с cgroup-лимитами на CPU (Kubernetes, systemd CPUQuota) троттлинг выглядит иначе - steal там может быть нулём, а тормоза реальны; ищи их в cgroup, об этом ниже.

Контекст-переключения (cs). Процессор умеет делать только одно дело за раз на ядро. Чтобы крутить сотни задач, ядро быстро переключает их: сохраняет состояние одной, грузит состояние другой. Это и есть контекст-свитч, и он не бесплатный - сбрасываются кеши и TLB процессора, тратятся такты впустую. Норма - единицы и десятки тысяч cs в секунду, это ок. А вот сотни тысяч cs при невысоком полезном us/sy - симптом: слишком много потоков дерутся за процессор, либо лок-контеншн, либо приложение лупит мелкими блокирующими операциями (например, синхронные сетевые запросы по одному). Смотри cs вместе с колонкой r: если r стабильно больше числа ядер - очередь на процессор переполнена, CPU реально насыщен (это уже CPU-bound).

Run queue и насыщение (2026, eBPF). Колонка r и load average говорят "очередь длинная", но не говорят, насколько больно от этого конкретным задачам. В 2026 это закрывают штатные eBPF-инструменты из пакетов bpfcc-tools и bpftrace, они есть в Astra/RED OS и обычных дистрибутивах:

Код: Выделить всё

runqlat 5 1        # гистограмма задержки ожидания в очереди (мкс)
runqlen 5 1        # длина очереди по ядрам
runqlat показывает, сколько микросекунд задачи реально стоят в очереди до того, как им дадут ядро. Если хвост гистограммы уезжает в миллисекунды - планировщик не справляется, CPU насыщен, и тут уже не помогут догадки по среднему. Это современная замена "смотреть только на r": eBPF в 2026 - зрелый штатный инструмент, а не экзотика.

PSI - давление на ресурсы (актуально на 2026). Самый честный сигнал насыщения сегодня - Pressure Stall Information, файлы в /proc/pressure/ (нужен CONFIG_PSI=y, в современных ядрах включён):

Код: Выделить всё

cat /proc/pressure/cpu
some avg10=23.10 avg60=18.40 avg300=9.05 total=84522331
Строка some показывает долю времени, в течение которого хотя бы одна задача ждала CPU вместо работы (avg за 10/60/300 секунд, total - микросекунды накопительно). Для CPU есть только some; для io и memory есть ещё full (когда все задачи стояли). PSI отвечает на вопрос "сколько производительности мы теряем из-за нехватки ресурса" точнее, чем load average. В cgroup v2 (а это дефолт в 2026) у каждого контейнера есть свои cpu.pressure, io.pressure, memory.pressure - именно так ловят троттлинг пода, когда steal=0, а сервис всё равно тормозит.

Как отличить CPU-bound от io-bound одной формулой:
  • Высокий us (плюс sy), idle низкий, wa низкий, r больше числа ядер, /proc/pressure/cpu some растёт - CPU-bound. Помогут профайлеры (perf из след. глав) и больше ядер/оптимизация горячего кода.
  • Высокий wa, b больше нуля, us низкий - io-bound. Подтверди через iostat, помогут диск, индексы, кеш. Ядра не спасут.
  • Высокий sy без явной причины - много системных вызовов или прерываний, дальше идём в strace или bpftrace по syscall.
  • Высокий st - проблема не у тебя, а у хоста; в контейнере вместо st смотри cpu.pressure и троттлинг.
На Astra Linux и RED OS всё это работает один в один - vmstat, mpstat, pidstat и top из пакетов procps-ng/sysstat те же самые, поля идентичны, PSI и eBPF-инструменты доступны на актуальных ядрах.

Типичные грабли и заблуждения
  • "CPU 100%, надо ядер" - сначала глянь, из чего эти 100%. Если там iowait - ядра не при делах.
  • Считать iowait долгом процессора. wa - это простой ядра, а не работа; и сам по себе он лишь подсказка, диагноз ставит iostat.
  • Смотреть только среднюю загрузку. Среднее по 16 ядрам спрячет одно перегруженное ядро. Всегда есть mpstat -P ALL и pidstat.
  • Путать load average и загрузку CPU. Load average учитывает ещё и процессы в io-ожидании (состояние D), поэтому high load при низком CPU - это часто диск, а не процессор.
  • Игнорировать steal на виртуалке и неделями оптимизировать свой код, который не виноват.
  • В контейнере искать steal вместо троттлинга. Лимит CPU в cgroup v2 душит сервис тихо - смотри cpu.pressure и cpu.stat (nr_throttled, throttled_usec).
  • Первая строка vmstat и первый интервал mpstat - это среднее за весь аптайм, по ним нельзя судить о текущем моменте.
Мини-лаба: сделай руками прямо сейчас
  • Запусти vmstat 1 в одном терминале. Во втором создай нагрузку на user:

    Код: Выделить всё

    yes > /dev/null &
    Смотри, как растёт us. Потом убей:
  • Создай нагрузку на iowait. Если есть пакет stress-ng:

    Код: Выделить всё

    stress-ng --io 4 --hdd 2 --timeout 30s
    Следи за колонкой wa и за b в vmstat, параллельно глянь

    Код: Выделить всё

    cat /proc/pressure/io
    Увидь своими глазами, что us при этом маленький.
  • Запусти mpstat -P ALL 1 и параллельно

    Код: Выделить всё

    yes > /dev/null &
    (один процесс). Найди ядро, которое улетело в 100% %usr, пока остальные отдыхают. Затем pidstat -u 1 - найди по имени сам процесс.
  • Перегрузи планировщик: запусти процессов yes больше, чем ядер, и посмотри

    Код: Выделить всё

    runqlat 5 1
    и

    Код: Выделить всё

    cat /proc/pressure/cpu
    - как растут задержка очереди и some.
  • Если ты на VPS - просто посмотри свой %steal в mpstat 1. Ноль это хорошо.
Контрольные вопросы
  • Почему при высоком iowait добавление ядер процессора обычно не помогает и почему wa - это подсказка, а не окончательный диагноз?
  • Чем режим user отличается от system, и о чём говорит стабильно высокий system?
  • Что означает steal, почему его невозможно убрать изнутри виртуальной машины и на что смотреть вместо него в контейнере с cgroup-лимитом?
  • Как по выводу vmstat (колонки r, b, wa, cs) и по /proc/pressure/cpu отличить CPU-bound нагрузку от io-bound?
Что запомнить

Загрузка CPU - это не одна цифра, а сумма режимов из /proc/stat. user - код приложения, system - ядро и системные вызовы, iowait - простой в ожидании io (а не работа процессора), steal - украдено гипервизором в виртуалке. Высокий sys гони в strace/bpftrace, высокий iowait - подтверждай через iostat и иди к диску, высокий steal - к провайдеру, высокий user при r больше числа ядер - это настоящий CPU-bound. Всегда смотри разбивку по ядрам через mpstat -P ALL и виновника через pidstat, а для честной картины насыщения в 2026 - /proc/pressure/cpu, runqlat и cpu.pressure контейнера.
👍6 ❤️1 🔥 😄 🤔1
Аватара пользователя
SparkNinja
Сообщения: 1
Зарегистрирован: 20 май 2026, 06:24

Re: Загрузка CPU: user, system, iowait, steal и контекст-свитчи

Сообщение SparkNinja »

Спасибо, наконец-то дошло про iowait. Месяц назад тимлид сказал что у нас CPU упирается, мы накинули ядер на сервер с базой - ноль эффекта. Теперь понятно, надо было на wa и iostat смотреть, а не на общую цифру.
👍1 ❤️ 🔥 😄 🤔
Аватара пользователя
efosse
Сообщения: 1
Зарегистрирован: 13 май 2026, 13:51

Re: Загрузка CPU: user, system, iowait, steal и контекст-свитчи

Сообщение efosse »

О, не знал про /proc/pressure/cpu. У нас сервис в k8s тормозил, а steal был ноль, мы голову ломали. Глянул cpu.pressure и cpu.stat - там throttled, оказалось CPU limit душит под. Снял лимит, полегчало.
👍 ❤️ 🔥 😄 🤔
Ответить
← Предыдущая глава
Сигналы и зависшие процессы: kill, и что делать с D-state
Следующая глава →
Диагностика CPU по ядрам: vmstat и mpstat

Все главы курса «Диагностика и производительность Linux: логи, strace, perf и eBPF»

Поделиться темой: ✈ Telegram VK
Похожие запросы: perf top как найти что грузит процессор

Вернуться в «Диагностика и производительность Linux: логи, strace, perf и eBPF»

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и 1 гость