MAATRIX / Блог / Почему перезагрузка действительно лечит: полный список того, что она сбрасывает

Почему перезагрузка действительно лечит: полный список того, что она сбрасывает

MAATRIX

«Выключите и включите снова» — фраза, над которой принято смеяться, но за ней стоит вполне конкретная инженерная логика. Перезагрузка не чинит код и не читает логи вместо вас, зато она гарантированно обнуляет несколько слоёв состояния сервера — память, процессы, файловые дескрипторы, накопленную деградацию ядра, — и именно эти слои чаще всего оказываются виновниками необъяснимых зависаний. Ниже — честный разбор того, что reboot действительно сбрасывает наверняка, а что остаётся ровно таким же, каким было до него.

Что происходит между командой reboot и новым ядром

Перезагрузка — это не одно действие, а последовательность шагов, и полезно понимать её целиком, чтобы видеть, где именно происходит «обнуление».

При штатном systemctl reboot (или просто reboot) события идут примерно так:

  1. systemd переводит систему в target reboot.target: каждому юниту посылается SIGTERM, даётся время на graceful shutdown (TimeoutStopSec), после чего недоответившим посылается SIGKILL.
  2. Файловые системы синхронизируются (sync) и размонтируются или переводятся в read-only — буферизованная в памяти запись физически попадает на диск.
  3. Ядро вызывает reboot(2), останавливает пользовательские процессы, выгружает модули там, где возможно, и передаёт управление на аппаратный сброс (или на новый kernel через kexec, если это soft reboot — тогда стадия firmware/BIOS пропускается).
  4. Firmware/BIOS проходит POST, загрузчик передаёт управление новому образу ядра.
  5. Ядро заново инициализирует все подсистемы — планировщик, менеджер памяти, сетевой стек, драйверы — с нуля, как будто сервер только что распаковали.
  6. PID 1 (systemd) стартует заново и поднимает сервисы в порядке зависимостей из unit-файлов.

Ключевой момент здесь — шаг 5: ядро не «продолжает» работу с того места, где остановилось, оно строит все свои внутренние структуры заново. Именно поэтому reboot — это не «пауза и продолжение», а полный сброс состояния, который вы не получите ни от systemctl restart конкретного сервиса, ни от kill -9 зависшего процесса. Если хотите понять, что происходит на самом старте, до появления пользовательского пространства — там своя механика: что сервер успевает сделать между подачей питания и появлением GRUB.

Память обнуляется полностью — и вместе с ней любая утечка

Это, пожалуй, самый частый и самый честный ответ на вопрос «почему reboot помогает». Оперативная память энергозависима: при перезагрузке всё её содержимое пропадает без остатка, включая:

  • кучу (heap) каждого процесса — вместе со всеми объектами, которые процесс аллоцировал, но забыл освободить;
  • страничный кеш, кеш inode и dentry — которые со временем накапливают гигабайты «горячих» файлов и метаданных, и иногда эта аккумуляция сама по себе начинает конкурировать с приложением за память;
  • любые внутренние буферы приложений — очереди задач, кеши сессий, пулы соединений — со всем мусором, который в них накопился.

Классический сценарий: процесс на каждый запрос аллоцирует немного памяти и не освобождает её полностью — либо из-за бага в коде, либо из-за фрагментации аллокатора в рантайме языка. RSS процесса растёт часы, дни, иногда недели, пока не упрётся в лимит cgroup, физическую память сервера или swap. Дальше либо OOM killer начинает убивать процессы (мы разбирали, как ядро выбирает жертву OOM killer), либо сервер уходит в тяжёлый своп и почти перестаёт отвечать.

Перезагрузка не находит и не чинит эту утечку — она просто обнуляет накопленный результат. Процесс запускается заново с пустой кучей, и до следующего накопления утечки пройдёт примерно столько же времени, сколько прошло в первый раз (если нагрузка сопоставима). Если вы подозреваете именно такой сценарий, полезнее не просто перезагружаться по расписанию, а научиться ловить утечку заранее — есть отдельный разбор, как поймать утечку памяти за неделю до падения, по трендам VmRSS и /proc/<pid>/status.

Что посмотреть до перезагрузки, если хотите понять масштаб:

free -h
ps aux --sort=-%mem | head -15
grep -i vmrss /proc/<pid>/status
cat /proc/meminfo | grep -E "Slab|SReclaimable|SUnreclaim"

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Арендовать сервер

Зависшие процессы и битые внутренние состояния исчезают вместе с ними

Второй слой — это не утечка памяти, а состояние, в котором процесс или поток застрял и не может из него выйти сам. Сюда относится несколько разных, но похожих по симптомам ситуаций:

  • Процессы в состоянии D (uninterruptible sleep) — обычно ждут ответа от устройства или сетевой файловой системы (NFS, повреждённый iSCSI-таргет, зависший блочный драйвер). Такой процесс не реагирует ни на kill -15, ни на kill -9, потому что он находится внутри системного вызова ядра и не может быть прерван, пока ядро само не получит ответ от устройства — а если устройство не отвечает никогда, процесс висит бесконечно.
  • Взаимные блокировки (deadlock) между потоками одного процесса — два потока ждут друг друга по мьютексам, и без вмешательства извне это состояние не разрешится никогда.
  • Пул соединений с «отравленной» записью — например, в приложении есть пул подключений к базе, и одно из соединений оказалось в невалидном состоянии после сетевого сбоя; дальше каждый запрос, которому достаётся именно это соединение, падает или подвисает, а само приложение не считает это поводом перезапустить пул.
  • Зомби-процессы, чей родитель не вызвал wait() и не освободил запись в таблице процессов — сами по себе не едят память и не грузят CPU, но засоряют таблицу процессов и иногда маскируют реальную проблему в родительском процессе (подробнее — почему зомби-процесс не ест память, но всё равно опасен).

Найти процессы в D-состоянии можно так:

ps aux | awk '$8 ~ /D/ {print}'

Всё это — состояние, которое существует только в памяти процесса или ядра и никогда не сохраняется на диск. Перезагрузка убивает всю таблицу процессов целиком и переинициализирует драйверы, на которых процесс висел, — поэтому она лечит зависание там, где точечный kill бессилен. Это не значит, что зависание нельзя было предотвратить (обычно можно — таймаутами на уровне приложения и на уровне драйвера), но постфактум единственный надёжный выход часто именно перезагрузка.

Файловые дескрипторы и сетевые соединения закрываются принудительно

Третий гарантированный эффект — на уровне таблиц ядра, которые ведут учёт открытых ресурсов.

У каждого процесса есть лимит на число одновременно открытых файловых дескрипторов (ulimit -n), и в него входят не только файлы, но и сокеты, pipe'ы, epoll-инстансы. Если приложение открывает файлы или соединения и не закрывает их — например, забывает закрыть файл после ошибки чтения, или не освобождает HTTP-клиент после таймаута, — дескрипторы накапливаются, пока процесс не упрётся в лимит и не начнёт получать EMFILE / «Too many open files» на каждую новую операцию. Мы разбирали симптомы этого отдельно: что происходит, когда кончаются файловые дескрипторы.

Проверить текущее состояние:

lsof -p <pid> | wc -l
cat /proc/<pid>/limits | grep "open files"
ss -tan state close-wait | wc -l

Отдельная категория — сетевые соединения, застрявшие в CLOSE_WAIT: приложение получило от удалённой стороны сигнал закрытия, но само забыло вызвать close(), и сокет годами (в масштабе аптайма) занимает запись в таблице соединений ядра. Если на сервере активен conntrack (обычно из-за iptables/nftables с stateful-правилами или NAT), у него есть собственный предел записей, и переполнение таблицы conntrack означает, что новые соединения начинают тихо отбрасываться:

sysctl net.netfilter.nf_conntrack_count
sysctl net.netfilter.nf_conntrack_max

Перезагрузка закрывает вообще все сокеты, освобождает вообще все дескрипторы и обнуляет таблицу conntrack целиком — она не «находит» утёкший дескриптор, она просто выбрасывает всю таблицу и строит её заново с нуля. Это грубее, чем точечное закрытие, но зато гарантированно полное.

Ядро без фрагментации: чем плох старый аптайм

Это наименее очевидный пункт, но именно он объясняет, почему иногда перезагрузка помогает даже там, где, казалось бы, ни памяти, ни дескрипторов не не хватало.

Ядро Linux управляет физической памятью через buddy-аллокатор (выдаёт непрерывные блоки страниц степени двойки) и slab/slub-аллокатор поверх него (для мелких объектов ядра — структур сокетов, inode, dentry и так далее). За долгий аптайм под нагрузкой физическая память может фраг­ментироваться: свободной памяти в сумме много, но не хватает непрерывных блоков нужного порядка. Это видно в dmesg как отказы аллокации высокого порядка, а посмотреть состояние можно так:

cat /proc/buddyinfo
slabtop -o | head -20

Практическое следствие: некоторым операциям ядра и драйверам нужны именно непрерывные многостраничные блоки — например, DMA-буферы у части сетевых и графических драйверов, hugepages, jumbo-фреймы на некоторых NIC. Если таких непрерывных блоков не находится, операция может деградировать (fallback на менее эффективный путь) или вовсе завершаться ошибкой, при том что в free -h память формально «есть». Похожую механику мы разбирали на уровне одного процесса — почему фрагментация памяти в долгоживущем процессе тоже съедает производительность без утечки как таковой; на уровне ядра логика та же, только структуры другие.

Кроме собственно фрагментации, за долгий аптайм в ядре накапливаются и другие вещи, которые reboot сбрасывает целиком: ARP- и маршрутные кеши, состояния TCP-таймстампов и окон для давно закрытых соединений, внутренние счётчики драйверов. Отдельная и неприятная категория — утечки памяти внутри самих драйверов ядра (не в userspace-приложении): такие баги случаются реже, чем прикладные утечки, но чинятся почти исключительно выгрузкой модуля или перезагрузкой, потому что безопасно выгрузить модуль, который держит активные структуры, часто просто нельзя.

Что перезагрузка не лечит — и когда проблема возвращается

Здесь важна честность: reboot — это симптоматическое лечение, а не диагностика. Он гарантированно обнуляет состояние, но не меняет ни код, ни конфигурацию, ни данные на диске. Отсюда несколько прямых следствий.

Если баг реальный — он повторится. Утечка памяти в коде, гонка состояний, которая срабатывает раз в несколько дней под определённым паттерном нагрузки, — не зависит от того, перезагружали вы сервер или нет. После reboot система снова начнёт накапливать то же самое состояние, и время до повторного сбоя будет примерно таким же (если нагрузка не изменилась). Перезагрузка по расписанию (каждую ночь через cron) в этом смысле работает не как лечение, а как способ держать симптом ниже порога срабатывания — законная тактика для временного смягчения, но не замена исправлению кода.

Конфигурация переживает reboot. Если причина — неверный параметр в конфиге, битый unit-файл systemd или неправильный лимит в /etc/security/limits.conf, после перезагрузки сервер либо повторит ту же ошибку, либо вообще не поднимется: проблемный конфиг читается заново при каждом старте, а не хранится «в памяти, которую мы обнулили».

Повреждение на диске reboot не чинит. Чистая перезагрузка (systemctl reboot, shutdown -r now) обычно синхронизирует файловые системы перед остановкой и сама по себе данные не портит. Но если сбой уже произошёл — например, приложение упало посреди записи в БД, — эта запись остаётся незавершённой и после чистого reboot; журналируемая ФС восстановит консистентность метаданных, но не логическую консистентность данных внутри приложения.

Перезагрузка стирает и улики. Это обратная сторона того же эффекта, который делает reboot полезным: обнуляя память и состояние процессов, вы теряете и саму возможность понять, что именно там было не так. Если сервер завис и вы хотите разобраться в причине, а не просто восстановить доступность, есть смысл снять диагностику до перезагрузки, а не после:

dmesg -T > /root/dmesg-before-reboot.log
ps auxf > /root/ps-before-reboot.log
ss -tan > /root/ss-before-reboot.log
journalctl -k -b > /root/kernel-boot.log

Если сервер не отвечает даже по SSH, единственный практичный вариант диагностики «постфактум» — посмотреть на журнал ядра уже после перезагрузки, из предыдущей сессии: journalctl -b -1 покажет логи предпоследнего запуска, если они успели дойти до диска. Разбор похожего случая — сервер сам перезагрузился: как понять, почему — там как раз про то, как восстанавливать картину задним числом.

И отдельно: reboot не патчит уязвимости сам по себе — если это не перезагрузка именно ради применения нового ядра после обновления безопасности (что, наоборот, абсолютно легитимная причина, потому что запущенное ядро продолжает работать со старым кодом в памяти, даже если пакет на диске уже обновлён).

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Арендовать сервер

Нужны сами нейросети для контента?

Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.

Частые вопросы

Помогает ли перезагрузка от 100% CPU?

Иногда — если причина в зависшем процессе, который не отдаёт CPU из-за внутреннего deadlock или busy-loop без выхода. Если же процессор нагружен реальной работой (легитимной нагрузкой или майнером после взлома), reboot даст лишь временную передышку до следующего цикла нагрузки.

Нужно ли перезагружать сервер после обновления пакетов?

Для большинства userspace-пакетов — нет, достаточно перезапустить сам сервис (systemctl restart <service>). Перезагрузка обязательна для нового ядра: запущенное ядро продолжает работать со старым кодом в памяти независимо от того, что лежит на диске, пока вы не загрузитесь заново (в отдельных случаях это решается live patching, но это отдельный механизм, а не стандартный путь).

В чём разница между systemctl restart сервиса и полной перезагрузкой сервера?

Restart сервиса обнуляет память, дескрипторы и состояние только этого процесса. Полная перезагрузка дополнительно сбрасывает состояние ядра — conntrack, фрагментацию аллокаторов, драйверы. Если проблема живёт на уровне процесса — обычно достаточно restart; если подозреваете уровень ядра или сети — нужен именно reboot.

Как часто стоит перезагружать сервер «для профилактики»?

Универсального правила без реальных цифр по вашей нагрузке не существует. Разумный подход — не гнаться за рекордным аптаймом, но и не перезагружать сервер вслепую по расписанию: лучше мониторить рост RSS, число открытых дескрипторов и conntrack_count, и перезагружать целенаправленно, когда метрики приближаются к лимитам, параллельно разбираясь в первопричине.

Что делать, если сервер завис и не реагирует даже на reboot по SSH?

Если SSH недоступен, штатная команда reboot тоже не сработает — остаётся аппаратный/панельный сброс через консоль хостинг-провайдера. Это жёсткий сброс без штатного размонтирования файловых систем, поэтому после него имеет смысл сразу проверить целостность файловой системы и логи загрузки.

Обсудить статью, задать вопрос или начать новую тему

Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.

Перейти в сообщество →