Усыпляем компьютер

1. Аннотация.
Заметка описывает настройку linux (ядра и ОС) для реализации «заморозки» текущего состояния системы. Описание производится на примере x86 Slackware Linux 10, ядра 2.6.14 и версии Suspend2 2.2-rc14.

2. Автор — Александр Андреев (aka San АНДРЕЕВ). Я не даю никаких гарантий (прямых, косвенных, подразумеваемых и т.д.), все нижеследующее вы соглашаетесь использовать исключительно на свой страх и риск. Со мной можно связаться через почту форума (требует регистрации).

3. Ключевые слова для поиска: linux hibernate software suspend acpi acpid slackware

4. Введение.
После появления поддержки железом различных режимов управления питанием и разработки соотв. программных средств, в т.ч. драйверов оборудования, поддерживающих управление питанием (как это делать описано напр. вLinux power management support), стало возможным производить «заморозку» текущего состояния программной среды с последующим быстрым его восстановлением. По достоинству это могут оценить владельцы ноутбуков — не надо закрывать приложения, сохранять документы, завершать работу специальным образом и т.д., достаточно просто закрыть крышку и система «застынет» в этом состоянии. Причем потерь данных при этом не происходит и энергия аккумуляторов не расходуется (в случае «засыпания» на диск). Для возвращения системы в исходное состояние достаточно просто включить ноутбук.

5. Исходные данные.
Поскольку на моем буке установлен дистрибутив Slackware Linux 10, то на нем (дистрибутиве) я и экспериментировал. Не думаю, что в других дистрибутивах должны быть какие-то принципиальные сложности, не исключено также, что в каких-то из них этот функционал по умолчанию включен, если вы об этом знаете — присылайте описание, включим в заметку.
Для реализации идеи понадобились исходные тексты ядра (я взял 2.6.14), патч для дозаточки механизма засыпания (я использовал Suspend2 2.2-rc14), скрипт усыпления системы (hibernate-1.12), средство под названием zenity для запуска диалогов Gnome из командной строки (опционально, моя версия 2.8.2), для zenity требуются popt, scrollkeeper, intltool, gtk±2.0, libglade-2.0, gconf-2.0 и libgnomecanvas-2.0, но они в дистрибутиве присутствуют и при необходимости их можно доустановить с диска.
Говоря о дозаточке, я имел в виду то, что в ядра 2.6.х механизм засыпания уже в основном встроен (заморозка процессов, периферийных и внутренних устройств и т.д.), патч только предоставляет дополнительные возможности по управлению этим механизмом.

6. Варианты сна.
Существует несколько вариантов засыпания (с вариациями). Первый и наименее сонный — это усыпление отдельных устройств (периферийных и внутренних) для снижения энергопотребления. Состояние системы в целом нигде не фиксируется, процессы остаются в памяти и работают в пределах железных ограничений (т.е. с использованием оставшегося не усыпленным железа).
Второй — с полным остановом системы и сохранением состояния в оперативной памяти. Энергопотребление здесь ниже, чем в первом случае, но все же имеется — память (RAM) обесточивать нельзя. С другой стороны, это самый быстрый способ усыпления, поскольку операции записи/чтения выполняются на самом быстром устройстве хранения (из доступных программно).
Третий — с полным остановом системы и сохранением состояния (проще говоря, дампа оперативной памяти) в файле либо устройстве (разделе) свопинга. Этот вариант не потребляет энергии, медленнее второго и требует наличия на диске swap-раздела или файла объемом желательно не меньше объема ОЗУ ПК. Объем сохраняемой при засыпании информации можно сократить, применив сжатие, но это скажется на времени засыпания и восстановления. Бонус — при сохранении в файл можно далее этот файл использовать как загрузочный образ на большом количестве одинаковых систем.

7. Патчим ядро.
Сначала свертесь с документацией на ваш дистрибутив — если патч Suspend2 уже наложен на умолчательное дистрибутивное ядро, либо такой вариант ядра просто имеется в дистрибутиве, то проще воспользоваться готовым, пропустить этот и следующие разделы и остановиться на доустановке hibernate (при необходимости) и конфигурировании демона acpid.
Каталог с патчем содержит также скрипты apply и unapply, которыми пользоваться вроде как должно быть удобнее и которые надо обязательно запускать из каталога исходных кодов ядра. Я поступил для себя проще - просто переписал сам патч в корень исходников ядра и наложил:
$ patch -p1 < 100-suspend2-2.2-rc14-for-2.6.14.patch

Если у вас есть какие-то вопросы по работе/использованию команды patch — почитайте документацию на нее (напр. man patch). Патч я накладывал на «чистое» (т.н. vanilla) ядро, поэтому никаких проблем (reject'ов) не возникло, все было «succeeded» (достигло цели то есть).

8. Конфигурируем ядро.
После наложения патча запускаем конфигуратор ядра и идем в раздел «Power management options (ACPI, APM)», в котором должен появиться пункт Suspend2. Если у вас была включена поддержка SMP (хотя бы для процессора с гипертредингом), то здесь может возникнуть неприятный момент — опции Suspend2 не будет. Согласно информации на сайте проекта, SMP-конфигурации вроде как поддерживаются, поэтому такая ситуация мне непонятна, однако ее можно обойти, если немного похачить. :)
От корня исходников ядра идем в kernel/power, открываем на редактирование файл Kconfig и в разделе «config SOFTWARE_SUSPEND» примерно в 32ой строке ( «depends on PM && SWAP …») надо убрать воскл. знак перед SMP после второй скобки и в самом конце строки. Эту процедуру надо повторить и примерно в 106ой строке (раздел «menuconfig SUSPEND2»). Теперь засыпание будет доступно только для конфигураций с SMP. Если вы вообще не хотите зависеть от мультипроцессорности, то в двух вышеуказанных строках упоминание про SMP надо убрать, чтобы они выглядели примерно следующим образом:
depends on PM && SWAP && X86 ||  (FVR || PPC32)
для строки 32 и
depends on PM 
для строки 106. В любом случае это хак, который может вызвать нестабильную работу системы.
Итак, опцию мы все-таки нарисовали, теперь ее надо включить и зайти в ее подменю. Там для начала достаточно выбрать пункт «Swap writer» и указать раздел со свопом, на который будет сохраняться состояние системы. Здесь все, не забываем не включать APM (acpi и apm использовать одновременно не надо), можно еще в «Device Drivers» → «USB support» от корня конфигурации выбрать пункт «USB suspend/resume» для поддержки замораживания USB-устройств.
Конфигурируем оставшееся в ядре если есть необходимость, сохраняем, пересобираем, ставим, дописываем в меню загрузчика если необходимо и перезагружаемся с новым ядром.

9. Настройка и проверка.
Если все прошло хорошо, то после загрузки нового ядра в /proc появится каталог suspend2 и в нем ряд файлов, в частности — do_suspend. Проверяем наличие на диске ('fdisk -l /dev/hdX | grep swap' от рута) и активность (напр. при помощи top или 'grep Swap /proc/meminfo') swap-раздела указанного выше объема (не меньше объема ОЗУ). Для автоматического задействования имеющегося свопа при загрузке системы достаточно прописать в /etc/fstab что-нибудь вроде такого:
/dev/hda2 none swap sw 0 0 

Теперь можно переключиться в чисто консольный runlevel (telinit 3 в каком-нибудь терминале от рута) и скомандовать
echo > /proc/suspend2/do_suspend

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

10. Аккуратное засыпание.
Аккуратное засыпание заключается в остановке процессов, отключении сетевых интерфейсов, выгрузке модулей и т.д. Всем этим занимается скрипт hibernate. Он почему-то написан с использованием zenity и мне оказалось проще доставить последнее, чем хачить первое. Не исключено также, что в чисто консольном режиме скрипт работает и без графических диалогов — я не пробовал. У этого скрипта имеется конфигурационный файл (в процессе установки кладется в /etc/hibernate вместе с другими файлами), в котором как раз и указывается что и как делать. Для моих целей особо переправлять его не пришлось, сам он находится во вложении к этой заметке. Для установки скрипта достаточно распаковать его архив и запустить файл install.sh. После правки конфига переходим к настройке собственно засыпания по разным поводам.

11. Настройка acpid.
Реагированием на все события, связанные с электропитанием, занимается демон acpid. Поэтому если мы хотим какой-то реакции системы на закрытие крышки, то надо демону это объяснить. Для начала идем в /etc/acpi/events и открываем на редактирование файл default, в котором комментируем все незакоментированное. После перезапускаем демона (для slackware — '/etc/rc.d/rc.acpid restart') и дальше наблюдаем за событиями в системе:
# tail -f /var/log/acpid

Нажимаем руками датчик закрытия крышки (или закрываем/открываем крышку). Должно появиться что-то вроде
[Thu Dec 8 01:12:04 2005] received event  «button/lid LID 00000080 00000003»

«button/lid» как раз и является интересующим нас событием, на которое нам в данном случае надо отреагировать. Обратно редактируем /etc/acpid/events/default и добавляем (или правим, если есть) в него след. сроки:
event=button/lid

action=/usr/local/sbin/hibernate

Здесь после event указывается отлавливаемое событие, а после action - что делать при появлении этого события, в данном случае запускается скрипт hibernate. Сохраняем, закрываем и снова перезапускаем acpid. Давим на датчик закрытия крышки — должен выскочить графический прогресс-бар, после мы должны вывалиться в консоль и дальше все как и при консольном засыпании. После выключения бука обратно его включаем, он сам подтягивает ядро, детектит железо, загружает из свопа образ памяти и запускает систему из замороженного состояния. Все.

12. Край для работы.
Интересной особенностью является сохранение дампа памяти в файл. Я с этим не экспериментировал, потому как нет надобности и времени, но если у кого есть успешный опыт — присылайте описание, добавим сюда.
Также любопытно настроить засыпание на разные события, напр. на разряд батареи — здесь можно почитать документацию на acpid и посмотреть на его логи.

13. Источники информации.
— man acpid
— www.suspend2.net/HOWTO
— www.opennet.ru/base/sys/hibernate.txt.html
— www.suspend2.net/downloads/
исходный код Suspend2 и hibernate
— directory.fsf.org/zenity.html
исходный код zenity
— san.linuxportal.ru/files/hibernate.conf
мой вариант hibernate.conf