Два разных инструмента: вежливость и потолок
Сразу разложим по полочкам, иначе будет каша. Есть два принципиально разных подхода.
- Вежливость (nice, ionice) - меняет порядок в очереди. Работает ТОЛЬКО когда есть конкуренция за ресурс. На простаивающей системе вежливый процесс заберет все. Жесткого потолка не дает.
- Потолок (cgroups: cpu.max, memory.max, io.max) - реальный лимит. "Не больше пол-ядра и 512 МБ, и точка". Работает всегда, даже если система простаивает.

Приоритет CPU: nice и renice в linux
Начнем с самого простого - nice. Представь очередь в столовой: планировщик ядра (на современных ядрах это EEVDF, пришедший на смену CFS в ядре 6.6) решает, кого пустить к процессору следующим. Значение nice - твоя вежливость в этой очереди. Диапазон от -20 до 19. Чем БОЛЬШЕ число, тем процесс вежливее (nice = "хороший, уступчивый"), тем меньше доля CPU при конкуренции. Чем меньше (вплоть до -20) - тем он наглее и прожорливее.
Важно понять: nice не выключает процесс и не дает ему ровно "20% CPU". Если система простаивает, даже nice 19 получит все ядра. Приоритет работает только при конкуренции. Тогда ядро делит время пропорционально весу: каждая единица nice меняет вес примерно в 1.25 раза, так что разница между nice 0 и nice 19 - это разница долей CPU в десятки раз.
Запустить программу с пониженным приоритетом:
Код: Выделить всё
nice -n 10 ./heavy_script.sh
Код: Выделить всё
renice -n 15 -p 24917
Код: Выделить всё
sudo renice -n -5 -p 24917
Где смотреть текущий nice? В выводе top это колонка NI, рядом - PR (реальный приоритет ядра). Или через ps:
Код: Выделить всё
ps -o pid,ni,pri,comm -p 24917
PID NI PRI COMMAND
24917 15 4 heavy_script.sh
Приоритет диска: ionice
nice управляет процессором, но часто тормозит не CPU, а диск. Бэкап читает терабайты, и интерактивная база начинает лагать на ровном месте. Тут нужен ionice - приоритет ввода-вывода. Важный нюанс 2026 года: ionice работает не на любом дисковом планировщике. Приоритеты ввода-вывода поддерживают BFQ и mq-deadline, а вот планировщик none (типичный дефолт для NVMe) их полностью игнорирует. Старый CFQ, на котором ionice исторически и появился, из ядра давно выпилен (еще в районе 5.0), его наследник - BFQ.
Три класса, задаются флагом -c:
- Realtime (-c 1) - получает диск первым, может задушить все остальное. Только root, использовать осторожно.
- Best-effort (-c 2) - обычный класс по умолчанию, внутри уровни приоритета -n от 0 (главный) до 7 (последний).
- Idle (-c 3) - диск только когда никто другой его не просит. Идеально для бэкапов: уровень -n тут не задается, класс сам по себе самый низкий.
Код: Выделить всё
sudo ionice -c 3 nice -n 19 tar -czf /backup/data.tgz /var/data
Код: Выделить всё
ionice -p 24917
idle
Перед тем как полагаться на ionice, проверь планировщик диска - это и есть главный подводный камень темы:
Код: Выделить всё
cat /sys/block/nvme0n1/queue/scheduler
[none] mq-deadline kyber bfq
Код: Выделить всё
echo bfq | sudo tee /sys/block/nvme0n1/queue/scheduler
cgroups v2: настоящие лимиты ресурсов в linux
nice и ionice - это про вежливость, а не про жесткий потолок. Если нужно сказать "этому процессу НЕ БОЛЬШЕ 50% ядра и 512 МБ памяти" - это cgroups (control groups). Это фундамент: именно через cgroups Docker, Kubernetes и systemd навешивают лимиты на контейнеры и сервисы. На современных дистрибутивах (2026) cgroups v2 - дефолт: единое дерево в /sys/fs/cgroup без разнесенных подпапок cpu, memory, blkio как было в v1.
Идея простая: группа процессов = папка. В папке лежат файлы-ручки, в которые пишешь лимит. Самые важные:
- cpu.max - квота CPU. Формат "квота период" в микросекундах. "50000 100000" значит 50 мс из каждых 100 мс = пол-ядра. Значение "max 100000" - без лимита.
- cpu.weight - относительный вес при конкуренции (1..10000, дефолт 100). Это и есть cgroup-аналог nice: вежливость, а не потолок.
- memory.max - жесткий потолок памяти. Превысил и не смог освободить - срабатывает OOM-killer внутри группы.
- memory.high - мягкий порог: память не режется насмерть, но процесс притормаживают и заставляют активно освобождать (reclaim, своп).
- io.max - лимит диска по устройству (major:minor), например rbps/wbps - байт в секунду на чтение/запись, riops/wiops - операций в секунду.
- io.weight - относительный вес ввода-вывода (только при активном BFQ).
Код: Выделить всё
sudo systemctl set-property nginx.service CPUQuota=50% MemoryMax=512M
Код: Выделить всё
sudo systemd-run --scope -p CPUQuota=30% -p MemoryMax=300M ./heavy_script.sh
Код: Выделить всё
sudo systemd-cgtop
Control Group Tasks %CPU Memory Input/s Output/s
/ 412 88.3 5.1G - -
/system.slice/mysql.service 38 61.2 2.3G - -
/system.slice/nginx.service 12 12.0 118.4M - -
Диагностика throttling и лимиты процесса (ulimit)
Поставил CPUQuota - и сервис вдруг стал медленным и дерганым? Скорее всего его душит throttling: задача уперлась в квоту, и ядро тормозит ее до конца периода (по умолчанию 100 мс). Это видно в файле cpu.stat внутри cgroup:
Код: Выделить всё
cat /sys/fs/cgroup/system.slice/nginx.service/cpu.stat
usage_usec 9234112
user_usec 7120044
system_usec 2114068
nr_periods 8423
nr_throttled 5901
throttled_usec 271044000
Отдельная история - ulimit: лимиты на один процесс и его потомков (открытые файлы, размер памяти, число процессов). Классика - too many open files:
Код: Выделить всё
ulimit -n
1024
Код: Выделить всё
cat /proc/24917/limits
Грабли и заблуждения
- nice не "выделяет проценты CPU". На свободной системе nice 19 заберет все ядра. Жесткий потолок - только cgroups (CPUQuota / cpu.max).
- ionice молча не работает на планировщике none (частый дефолт для NVMe). Приоритеты понимают только BFQ и mq-deadline. Проверь cat /sys/block/.../queue/scheduler перед тем как рассчитывать на ionice.
- Старого CFQ в современном ядре уже нет, его место занял BFQ. Гайды, которые говорят "включите CFQ для ionice", устарели.
- Слишком тесный CPUQuota хуже его отсутствия: получишь throttling и рваные тормоза вместо плавного замедления. Всегда сверяйся с cpu.stat. Часто правильнее не жесткий CPUQuota, а относительный CPUWeight - тогда сервис притормаживается только при реальной конкуренции.
- memory.max - это жестко: процесс не "затормозит", а получит OOM внутри своей группы. Для мягкого давления бери memory.high (MemoryHigh).
- limits.conf не действует на systemd-сервисы - правь LimitNOFILE в юните. Очень частая ловушка.
- На Astra Linux и RED OS systemd и cgroups v2 на месте, все описанное работает. Если /sys/fs/cgroup выглядит иначе (старое дерево с подпапками cpu, memory, blkio) - система на cgroups v1, имена файлов другие (cpu.cfs_quota_us и т.п.).
- Запусти нагрузку: yes > /dev/null & и посмотри в top - оно ест 100% ядра. Сделай ему renice -n 19 -p PID и запусти рядом второй yes без nice - увидишь в top, как они делят CPU резко неравномерно. Останови, запусти только один с nice 19 - он снова заберет все ядро (конкуренции нет).
- Загони ту же нагрузку в лимит: sudo systemd-run --scope -p CPUQuota=20% yes > /dev/null - теперь больше 20% ядра не возьмет, как ни старайся.
- Найди cgroup этого scope в /sys/fs/cgroup (имя вида run-rXXXX.scope) и читай его cpu.stat в цикле - убедись, что nr_throttled растет. Проверь scheduler своего диска: cat /sys/block/.../queue/scheduler. Не забудь убить yes после опытов: pkill yes.
- Чем принципиально отличается nice (или cpu.weight) от CPUQuota в cgroups? Когда какой инструмент брать?
- Каким классом ionice запустить ночной бэкап, чтобы он не мешал базе, и что обязательно проверить, прежде чем на ionice полагаться?
- По каким двум полям cpu.stat понять, что сервис душит throttling, и какой порог уже тревожный?
- Что произойдет с процессом при превышении memory.max и чем это отличается от memory.high?
nice и ionice - это вежливость: работают только при конкуренции за ресурс и не дают жесткого потолка. cgroups v2 - это настоящие лимиты (cpu.max, memory.max, io.max) и относительные веса (cpu.weight, io.weight), и именно на них стоят Docker, Kubernetes и systemd. Притормозить жадный процесс, не убивая, проще всего через renice, ionice -c 3 (если планировщик это поддерживает) или systemctl set-property с CPUQuota/CPUWeight. А если после лимита все затормозило - первым делом смотри nr_throttled в cpu.stat: тесный лимит лечится не сильнее, а аккуратнее.