Гибернация - единственный способ выключить ноутбук с открытым проектом и вернуться к нему через неделю в том же виде, но в Linux она почти никогда не работает сразу из коробки. Ядро обязано найти своп, прочитать из него образ памяти и продолжить жизнь ровно с того места, где машина уснула. Раздел подкачки и файл подкачки ведут себя здесь по-разному, а загрузка через UEFI добавляет свои сюрпризы: от автоматической переменной HibernateLocation до полного запрета гибернации при включенной проверке подписей. Ниже - рабочий разбор обоих вариантов с точными командами и списком ловушек, проверенных на практике.
Почему выбор между файлом и разделом вообще важен
При засыпании ядро пишет содержимое памяти на устройство подкачки, а при пробуждении читает образ обратно до монтирования корневой файловой системы. Отсюда жесткое требование: место отдыха обязано быть доступно на самой ранней стадии загрузки, и ядро должно знать его адрес заранее. Раздел дает ядру стабильный адрес - UUID, который не меняется годами. Файл дает гибкость: его создают без переразметки на живой системе, увеличивают и уменьшают, а платой за гибкость становится второй параметр - смещение файла внутри раздела, без которого ядро не поймет, с какого блока читать образ. Ошибка в смещении выглядит обманчиво безобидно: загрузка проходит, система стартует с чистого листа, а несохраненная сессия исчезает без всяких сообщений об ошибке. Размер свопа выбирают не меньше объема памяти: образ сжимается на лету, но запас никогда не мешает, а памяти в современных ноутбуках бывает 32 и 64 ГиБ. От сна гибернация отличается радикально: питание снимается целиком, батарея не расходуется ни грамма, и неделя в рюкзаке не меняет состояние системы. Хранителем образа служит только область подкачки, поэтому без достаточного свопа говорить о гибернации вообще бессмысленно, а всю остальную настройку ядра и загрузчика честно выполняют до первых экспериментов с кнопками.
Подготовка раздела подкачки и параметра resume
Для раздела вся настройка сводится к одному параметру ядра. UUID устройства подкачки проще всего найти в выводе blkid с фильтром по слову swap: строка сразу показывает и раздел, и TYPE=swap с готовым идентификатором, после чего параметр попадает в /etc/default/grub:
GRUB_CMDLINE_LINUX_DEFAULT="quiet splash resume=UUID=4209c845-f495-4c43-8a03-5363dd433153"
После правки выполняется update-grub, и в Debian с Ubuntu параметр дополнительно дублируется в конфигурацию initramfs, потому что часть проверок происходит еще до монтирования корня:
echo "RESUME=UUID=4209c845-f495-4c43-8a03-5363dd433153" | sudo tee /etc/initramfs-tools/conf.d/resume
sudo update-initramfs -u
Проверка без перезагрузки выглядит непривычно, но честно: /sys/power/resume показывает пару major:minor устройства, которое ядро приняло к сведению, а /sys/power/state перечисляет доступные состояния. Активные области подкачки со своими приоритетами перечисляет swapon --show:
swapon --show
NAME TYPE SIZE USED PRIO
/dev/sda2 partition 34G 0B -2
Значение PRIO по умолчанию отрицательное, и это нормально для дисковой области: высокий положительный приоритет резервируют для zram. Если в файле стоит нулевой адрес, параметр до ядра не доехал, и поиск ошибки начинают с update-grub и содержимого grub.cfg. Для систем на LVM параметр указывает на логический том вида /dev/mapper/vg-swap, и работает так же надежно, как UUID.
Файл подкачки со смещением на обычной файловой системе
Файл создают заполнением с нуля, потому что разреженные файлы для гибернации непригодны:
sudo fallocate -l 32G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
Дальше - сердце настройки, вычисление смещения первого блока файла:
sudo filefrag -v /swapfile | awk '{ if($1=="0"){ print substr($4, 1, length($4)-2) } }'
Команда печатает номер физического блока, например 38912, и это значение идет в параметр resume_offset, а параметр resume при файле указывает на UUID того раздела, где файл лежит. Несколько экстентов в выводе filefrag - признак фрагментации, и файл тогда пересоздают через dd на свежем месте диска: надежнее всего ядро читает непрерывные области. Права 600 на файле стоят не для красоты: образ памяти содержит все, что было в RAM, включая пароли и ключи, и читать его посторонним нельзя. Оба параметра прописываются рядом в /etc/default/grub и в initramfs, после чего update-initramfs -u обновляет загрузочный образ. На Btrfs схема длиннее: файл подкачки обязан жить на отдельном подтоме с отключенным копированием при записи, атрибут ставят на пустой каталог командой chattr +C до создания файла, а смещение выдает btrfs inspect-internal map-swapfile -r /swap/swapfile. Поддержка своп-файлов на Btrfs появилась в ядре 5.0, так что старые системы вынуждены оставаться на разделе. Раздел и файл спокойно живут в одной системе: оба активируются через swapon, приоритет расставляет ключ -p, а гибернации назначают один конкретный адрес параметром resume. Каждое пересоздание файла меняет его смещение, поэтому после mkswap значение resume_offset сверяют заново, иначе следующее пробуждение промахнется мимо образа.
Роль systemd в восстановлении на UEFI системах
Свежий systemd сильно упростил жизнь владельцам UEFI-машин: перед засыпанием служба systemd-sleep сама выбирает подходящее устройство подкачки и записывает его в EFI-переменную HibernateLocation, а при следующей загрузке компонент systemd-hibernate-resume читает эту переменную и кормит ядро нужным адресом. На таких системах параметры resume и resume_offset вручную вообще не обязательны, и гибернация работает сразу после появления подходящего свопа. Проверить наличие механизма можно записью /sys/power/resume после сна без параметров: если ядро видит правильное устройство, автоматика трудится. Ориентир для классификации системы - каталог /sys/firmware/efi: на настоящей UEFI-загрузке он существует и хранит efivars, а на классической BIOS-загрузке его нет, и автоматика переменных недоступна по определению. Ручные параметры при этом не вредят и остаются страховкой для случаев, когда systemd постарее или переменную кто-то затер. Туда же просится настройка крышки ноутбука: в /etc/systemd/logind.conf значениям HandleLidSwitch и HandleLidSwitchClosedExternalMonitor назначают suspend-then-hibernate, и закрытие крышки сначала усыпляет систему в память, а через заданный интервал перекладывает образ на диск. Для рабочего ноутбука режим выглядит идеальным: часовая поездка на обед тратит проценты батареи, а вечер в дороге уже ничего не расходует. Ловушка прячется рядом: после переразметки диска старая переменная указывает в пустоту, восстановление молча не случается, и систему прощают за то, в чем виноват собственный прошлогодний эксперимент с разделами.
Secure Boot, lockdown и другие ловушки UEFI
Самая неприятная ловушка связана с проверкой подписей. При включенном Secure Boot ядро переходит в режим блокировки, а в этом режиме гибернация запрещена намеренно: образ свопа лежит на диске без защиты, и подмененный образ мог бы получить контроль над системой на этапе загрузки. Ядро честно сообщает в журнале, что гибернация ограничена режимом блокировки, а Ubuntu дополнительно прячет кнопку из меню питания через polkit. Выходов два: отключить Secure Boot в прошивке либо мириться со сном без гибернации, и для многих рабочих ноутбуков второй вариант оказывается честным компромиссом. Состояние проверки до и после манипуляций показывает mokutil --sb-state, и перезагрузка между сменами режима обязательна. Выбирая сторону, стоит понимать цену: без подписи образ гибернации на диске может заменить любой, кто получит физический доступ к машине, поэтому ноутбуки с чувствительными материалами оставляют на сну без сворачивания на диск. Кнопку в Ubuntu возвращают файлом политики, а в новых версиях polkit та же задача решается правилом в JavaScript-синтаксисе в каталоге rules.d:
polkit.addRule(function(action, subject) {
if (action.id == "org.freedesktop.login1.hibernate" && subject.isInGroup("sudo")) {
return polkit.Result.YES;
}
});
Файл кладут в /etc/polkit-1/rules.d с расширением rules и правами root, и кнопка гибернации возвращается в меню питания без перезагрузки:
sudo tee /etc/polkit-1/localauthority/50-local.d/com.ubuntu.enable-hibernate.pkla <<EOF
[Enable hibernate]
Identity=unix-user:*
Action=org.freedesktop.login1.hibernate
ResultActive=yes
EOF
Вторая ловушка касается двойной загрузки: быстрый старт Windows не выключает систему по-настоящему, а укладывает ее в гибернацию, и на диске оказываются два независимых состояния питания сразу. Общие разделы после этого монтируются с ошибками, поэтому быстрый старт в Windows отключают в настройках электропитания до любых экспериментов со сном Linux.
Проверка гибернации и диагностика неудач
Первый прогон делают из терминала, без участия графической кнопки:
systemctl hibernate
Экран гаснет, машина выключается, а включение должно вернуть сессию целиком: ядро находит образ в свопе еще до монтирования корня и восстанавливает память за считанные секунды. Перед первым тестом работу с важными файлами закрывают, чтобы тихий отказ не обернулся потерями, и держат под рукой второй доступ к машине, например ssh, для разбора полетов. Сквозная диагностика при неудаче выглядит так:
- Выполнить systemctl hibernate и дождаться завершения записи образа;
- Включить питание и наблюдать за сообщениями загрузки ядра;
- Сверить /sys/power/resume и /sys/power/resume_offset с ожидаемыми значениями;
- Открыть journalctl -b -1 и искать строки с пометкой PM о ходе засыпания;
- Сравнить размер свопа из swapon --show с объемом установленной памяти;
- Исправить параметры и обновить initramfs командой update-initramfs -u.
Перед диагностикой убеждаются, что ядро вообще умеет гибернацию: в файле /sys/power/state должно быть слово disk. Если его нет, ядро собрано без поддержки, и никакие параметры не помогут до смены ядра. Типовых причин отказа немного, и все они проверяются быстро. Своп меньше памяти означает тихий отказ с сообщением о нехватке места. Активный zram с высоким приоритетом сбивает с толку службу выбора цели, и без явного resume попытка сворачивается в память, которая исчезнет вместе с питанием. Устаревший resume_offset после пересоздания файла уводит чтение в чужие блоки, и ядро молча продолжает обычную загрузку. Составной режим suspend-then-hibernate настраивается в /etc/systemd/sleep.conf параметром HibernateDelaySec и усыпляет машину сначала в память, а через заданный интервал перекладывает на диск.
Сравнение вариантов и практический выбор
Раздел подкачки выигрывает предсказуемостью: один UUID, ноль вычислений, работающий механизм на любой файловой системе и ядрах любой свежести. Его цена - жесткость: изменение размера тянет за собой переразметку, а место под раздел в эпоху 64 ГиБ памяти иногда выглядит нелепо. Файл выигрывает гибкостью и лояльностью к Btrfs-схемам с подтомами, а платит двумя числами, которые обязаны жить в согласии: адресом раздела и смещением файла, и каждое из них ломается при пересоздании файла. Практический рецепт выглядит скучно и потому работает: на новых установках со свежим systemd доверяют автоматике с файлом подкачки, на старых системах и при двойной загрузке с Windows выбирают раздел, а смещение и UUID документируют в личной заметке рядом с описанием разметки. Объем файла подкачки на диске в полтерабайта и памяти в 16-32 ГиБ незаметен, а текущий баланс памяти и свопа показывает free -h в любой момент. Серверам гибернация не нужна вовсе: там своп существует для пиковых нагрузок, а перезагрузка после обслуживания занимает минуты и не тянет за собой образ памяти. Гибернация при таком подходе перестает быть лотереей: машина засыпает на диск по кнопке, просыпается с открытыми вкладками и не требует ни переустановки, ни танцев вокруг прошивки.