Der Linux-Kernel implementiert unter dem Stichwort System Sleep eine Zwangsabschaltung. Für Smartphones und andere Mobilgeräte ist diese Stromspartechnik unentbehrlich.
Wer sich unter Ubuntu 18.04 nach einem Hibernate-Button umsieht, der sucht vergeblich. Die Funktion, die eine Kopie des gerade aktiven Systems im Swap-Bereich der SSD oder altmodischen Festplatte ablegt und beim nächsten Start restauriert, arbeitet zu instabil und funktioniert auf einigen Rechnern nicht zufriedenstellend, so die Begründung. Das überrascht nicht, zieht man die Komplexität der Aufgabe in Betracht.
Es gilt, den Systemzustand penibel zum aktuellen Zeitpunkt zu erfassen, ihn einzufrieren, ein Hauptspeicherabbild zu erstellen und es auf die SSD in den Swap-Bereich zu schreiben. Auf den Swap-Bereich kann das System jedoch erst einmal nicht mehr zugreifen, denn die Geräte sind ja eingefroren. Also heißt es: Geräte auftauen, Image speichern und alles erneut einschläfern.
Beim Hochfahren erfolgt die Rolle rückwärts (Abbildung 1): Das System muss den Kernel laden und ihn mit der Kopie aus dem Massenspeicher überschreiben – mit dem Reboot haben sich Adressen geändert. Dann steht eine Restaurierung des Userlands an. Zu guter Letzt erhält der eigentliche, sogenannte Image-Kernel die Kontrolle. Dass möglicherweise zwischendurch jemand das zuvor eingesteckte USB-Laufwerk entfernt hat, macht die Sache keinesfalls einfacher.

Abbildung 1: Das Aufwecken übernimmt ein Restore-Kernel als Wachmacher.
Genug ist genug
Da der energetische Nutzen von Suspend-to-Disk (Hibernation) gegenüber Suspend-to-RAM (Suspend) für die Dauer einer Kaffeepause überschaubar gering ausfällt, haben die Ubuntu-Maintainer beschlossen, sich nicht länger mit der komplexen Technik herumzuärgern. Wer das System über einen längeren Zeitraum einfriert, profitiert allerdings von Hibernation. Neugierige Admins und Anwender, die auch noch das letzte Watt sparen möchten, lassen sich ebenfalls nicht abhalten, Suspend-to-Disk zu aktivieren. Das lässt sich nämlich mit wenigen Handgriffen bewerkstelligen und erledigt auf nicht exotischen Systemen auch zuverlässig seinen Dienst (siehe Kasten “Hibernation unter Ubuntu 18.04”).
Suspend und Hibernation stellen als System-Power-Management beziehungsweise System Sleep neben Runtime-Power-Management die zweite Variante des Linux-Power-Managements dar [1]. Während beim Runtime-PM [2] Treiber während des normalen Betriebs freiwillig nicht benötigte Geräte herunterfahren und so den Energieverbrauch den aktuellen Anforderungen anpassen, erzwingt System-PM für sämtliche Systemteile den Stromsparzustand. Zwar dürfen Spotify, Kameras und Netzwerkkarten aktiv sein, doch sobald der Anwender den Deckel des Notebooks zuklappt oder der Admin »systemctl hibernate« eintippt, muss Ruhe einkehren.
Hibernation unter Ubuntu 18.04
Um ein Ubuntu 18.04 per Suspend-to-Disk schlafen zu legen, gilt es, Grub umzukonfigurieren: Grub muss dem (Restore-)Kernel beim Booten mitteilen, auf welcher Partition er ein potenzielles Hauptspeicherabbild findet. Auf einem normal installierten System (ohne LVM und ähnlichem) ruft der Anwender dazu folgendes Kommando auf:
$ blkid | grep swap | cut -f 2 -d " "
Mit dem Schlüsselwort »resume=« und dem ohne Leerzeichen angehängten Ergebnis des Kommandos ergänzt er dann in der Datei »/etc/default/grub« den Eintrag »GRUB_CMDLINE_LINUX_DEFAULT«. Steht dort bereits »quiet splash«, sieht die für jedes System individuelle Zeile anschließend ähnlich wie die folgende aus:
GRUB_CMDLINE_LINUX_DEFAULT="quiet splash resume=UUID=ddaabf16-c18a-49af-aacc-c5d65c389704"
Neugierige lassen noch die Schlüsselwörter »quiet« und »splash« weg, um dem Kernel beim Booten zusehen zu können. Ein anschließendes »sudo update-grub« auf der Konsole konfiguriert den Bootloader entsprechend.
Um den Erfolg zu testen, tippt der Nutzer auf der Konsole als Superuser das Kommando »systemctl hibernate«. Nach einigen Sekunden sollte sich der Rechner abschalten. Beim nächsten Start lädt das System den Restore-Kernel, der seinerseits das Image aus dem Swapspace in den Arbeitsspeicher wuchtet. Eine Anleitung, um als Sahnetüpfelchen noch einen Hibernate-Button in die Gnome-Oberfläche zu integrieren, findet sich auf Ubuntuhandbook.org [4].
Hinter den Kulissen
Dazu fordert der Kernel jedes Gerät auf, in den Stromsparzustand zu wechseln und sich nach Möglichkeit komplett stromlos zu schalten. Wie das im Detail erfolgt, ist gerätespezifisch in den Tiefen der zugehörigen Gerätetreiber implementiert [3]. Das System schaltet die Devices im Übrigen in exakt der umgekehrten Reihenfolge ab, in der sie sich beim Kernel angemeldet haben; beim Aufwecken läuft alles in entgegengesetzter Richtung ab.
Um die Komplexität der Aufgabenstellung zu meistern, gehen das Schlafenlegen und Aufwecken in mehreren Phasen vonstatten, die allerdings von der Art des Schlafens abhängen (siehe Kasten “Suspend-to-Was?”). Für jede Phase deklariert die Struktur »dev_pm_ops« einen spezifischen Callback (Listing 1).
Listing 1
System-PM über 23 Callbacks
struct dev_pm_ops {
int (*prepare)(struct device *dev);
void (*complete)(struct device *dev);
int (*suspend)(struct device *dev);
int (*resume)(struct device *dev);
int (*freeze)(struct device *dev);
int (*thaw)(struct device *dev);
int (*poweroff)(struct device *dev);
int (*restore)(struct device *dev);
int (*suspend_late)(struct device *dev);
int (*resume_early)(struct device *dev);
int (*freeze_late)(struct device *dev);
int (*thaw_early)(struct device *dev);
int (*poweroff_late)(struct device *dev);
int (*restore_early)(struct device *dev);
int (*suspend_noirq)(struct device *dev);
int (*resume_noirq)(struct device *dev);
int (*freeze_noirq)(struct device *dev);
int (*thaw_noirq)(struct device *dev);
int (*poweroff_noirq)(struct device *dev);
int (*restore_noirq)(struct device *dev);
int (*runtime_suspend)(struct device *dev);
int (*runtime_resume)(struct device *dev);
int (*runtime_idle)(struct device *dev);
};
Soll das System in den Zustand Suspend-to-Idle oder Suspend-to-RAM gehen, durchläuft es die Phasen »prepare«, »suspend«, »suspend_late« und »suspend_noirq« (Abbildung 2). Dabei signalisiert »prepare« dem Treiber, dass ein Wechsel in einen Stromsparmodus ansteht. Der Treiber sorgt im Rahmen des zugehörigen Callbacks dafür, dass keine zusätzlichen (zum Beispiel neu angesteckten) Geräte bedient werden.

Abbildung 2: Das Speicherschlafen läuft in den vier Phasen »prepare«, »suspend«, »suspend_late« und »suspend_noirq« ab.
Ein Gerätetreiber stoppt im Rahmen des Callbacks »suspend« alle I/O-Zugriffe. Die Phase »suspend_late« fehlt in älteren Kernel-Versionen. In ihr sichert das System den aktuellen Zustand eines Geräts, das es anschließend in den Stromsparzustand überführt.
Der Callback »suspend_noirq« kommt schließlich bei gesperrten Interrupts zum Einsatz. Teilen sich beispielsweise zwei Geräte einen Interrupt, gelingen das Retten des Gerätezustands und das Überführen in den Stromsparzustand nicht immer. Hier springt dann dieser Callback in die Bresche.
So oder so: Zu guter Letzt haben die Gerätetreiber bei all ihren Geräten die Ein-/Ausgabeoperationen gestoppt (namentlich DMA und Interrupts), die Gerätezustände so gesichert, dass sie sich wiederherstellen lassen und die Geräte in den Schlaf versetzt. Dazu muss nicht jeder Callback implementiert werden: Viele Treiber verwenden nur ein »suspend_late« für ihre Geräte.
Suspend-to-Was?
System-PM unterscheidet im Wesentlichen vier unterschiedliche Schlafenszustände (Abbildung 3): Suspend-to-Idle (S2Idle), Standby, Suspend-to-RAM (STR, S2RAM) und Hibernation (Suspend-to-Disk, STD).
Suspend-to-Idle (S2Idle) versucht dadurch Energie einzusparen, dass es die CPU möglichst weitgehend von Rechenarbeit freistellt – auf modernen Systemen beispielsweise durch Heruntertakten des Prozessors. Dazu friert das System sämtliche Applikationen ein, also das komplette Userland. Zusätzlich deaktiviert es bei Standby die Zeitverwaltung und versetzt Ein-/Ausgabegeräte in ihren Low-Power-Mode. Sämtliche CPU-Kerne mit Ausnahme des für das Booten zuständigen werden abgeschaltet. Auf Systemen, die ACPI unterstützen, entspricht dieser Zustand dem Zustand S1.
Das von den Entwicklern favorisierte Suspend-to-RAM friert den Systemzustand im Hauptspeicher ein, bevor es die Stromversorgung mit Ausnahme der des Speichers kappt. Dieser Zustand entspricht auf ACPI-Systemen der Ebene S3. Auch Suspend-to-RAM benötigte viele Jahre Entwicklung, bis es auf der überwiegenden Mehrheit der Geräte zufriedenstellend Strom sparte.
Bei Hibernation wird der Systemzustand auf dem Hintergrundspeicher abgelegt, typischerweise im Swap-Bereich einer SSD. Dazu muss der Swap-Bereich mindestens dieselbe Größe haben wie der Hauptspeicher. Beim Suspend-to-Disk durchläuft das System drei Phasen: Bei der Aktivierung stoppt der Kernel, deaktiviert sämtliche Geräte und erzeugt ein Hauptspeicherabbild. Dieses Image schreibt er im zweiten Schritt verschlüsselt auf den Swap-Bereich, wofür er die dazu benötigten Geräte wieder aktiviert. Schließlich deaktiviert er die in Anspruch genommenen Geräte wieder und überführt das System entweder in einen Low-Power-Zustand (ACPI S4) oder schaltet es komplett aus.
Das Aufwecken des Systems und Restaurieren des Ursprungszustands (Resume) läuft in zwei Phasen ab: Bei der Reaktivierung ergreift zunächst das BIOS beziehungsweise die Firmware die Initiative und aktiviert den Bootloader. Der bootet seinerseits einen Linux-Kernel (Restore-Kernel, Phase 1). Der Kernel überprüft den Swap-Bereich auf das Vorhandensein eines Hauptspeicherabbilds. Findet er eines, transferiert er es in den Hauptspeicher. Daraufhin überschreibt der Restore-Kernel sich im Hauptspeicher selbst und aktiviert den ursprünglichen Kernel (Image-Kernel) (Phase 2). Dieser Kernel restauriert schließlich das System.

Abbildung 3: Der Admin kann Linux in unterschiedliche Energiesparzustände versetzen.
Aufgewecktes Linux
Das Aufwachen findet in umgekehrter Reihenfolge statt: Auf »resume_noirq« und »resume_early« folgen »resume« und »complete«. Der Aufruf von »resume_noirq« erfolgt vor der Freigabe von Interrupts. Der Treiber kann also vorab für seine Geräte alles Notwendige veranlassen, um Probleme auszuschließen.
Der Callback »resume_early« dient dazu, alles rückabzuwickeln, was in »suspend_late« erledigt wurde, und insbesondere den Gerätezustand zu restaurieren. Im Rahmen des »resume«-Callbacks lässt der Treiber das Gerät seinen Dienst wieder aufnehmen, innerhalb von »complete« wird die volle Funktionstüchtigkeit wiederhergestellt. Am Ende des Vorgangs funktioniert alles wieder wie vor dem Schlafenlegen, speziell jegliche Ein-/Ausgabe.
Die Phasen beim Suspend-to-Disk (Hibernation) zeigt Abbildung 4. Die Callbacks »freeze«, »freeze_late« und »freeze_noirq« entsprechen mehr oder weniger den Callbacks »suspend«, »suspend_late« und »suspend_noirq«. Am Ende sind die zugehörigen Geräte ruhiggestellt, und das PM-Subsystem erstellt aus dem Inhalt des Hauptspeichers ein Image. Um das Abbild im Swap-Bereich ablegen zu können, braucht es die soeben narkotisierten Geräte, die also kurzfristig aufgeweckt respektive aufgetaut werden müssen.
In dieser Phase erfolgt der Aufruf der Callbacks »thaw_noirq«, »thaw_early«, »thaw« und »complete«. Sie arbeiten ähnlich wie die »resume«-Callbacks bei Suspend-to-RAM. Sobald damit der Zugriff auf die SSD oder Festplatte gelingt, sichert der Kernel das Image und leitet schließlich das finale Schlafen über die Callbacks »poweroff«, »poweroff_late« und »poweroff_noirq« ein.

Abbildung 4: Der stromlose Zwangsschlaf bei der Hibernation gerät besonders kompliziert.
Doppel-Kernel
Es erweist sich als recht anspruchsvoll, das System wieder aus dem Tiefschlaf zurückzuholen. Dabei kommen zwei Kernels zum Einsatz: der Wachmacher-Kernel, normalerweise Restore- oder Boot-Kernel genannt, sowie der Image-Kernel.
Der Restore-Kernel bewältigt die Aufgabe, das Hauptspeicherabbild vom Swapspace ins RAM zu wuchten und schließlich die Kontrolle an den ursprünglichen Image-Kernel zu übergeben. Letzterer müsste jetzt nur noch den Resume-Vorgang anstoßen. Hier haben die Kernel-Entwickler aber mit »restore_noirq«, »restore_early« und »restore« andere Callbacks vorgesehen. Der Hintergrund: Der Restore-Kernel hat die Geräte typischerweise für seine Belange initialisiert und möglicherweise schon genutzt. Damit ist deren Zustand unbekannt – die neuen Callbacks können darauf Rücksicht nehmen.
Die zunächst einfach und strukturiert aussehende Abfolge der Phasen fällt in der Realität in der Handhabung jedoch komplexer aus. Die Suspend- und Resume-Callbacks kommen beispielsweise nur dann zum Aufruf, wenn es keine Runtime-PM-Callbacks gibt. Restore-Callbacks werden in diesem Fall jedoch abgearbeitet. Anders als in der Dokumentation angegeben, erfolgt dabei augenscheinlich kein Aufruf des Callbacks »prepare«.
Den pfiffigen Entwickler stellt das aber nicht vor ein unlösbares Problem: Zusätzlich zu den genannten Callbacks können sich Gerätetreiber über sechs unterschiedliche Notifier vom Kernel über den Zustandswechsel informieren lassen, und zwar jeweils zu Beginn und zum Ende einer Phase. Die definierten PM-Notifier und deren Programmierung zeigt Listing 2.
Listing 2
Kernel-Demo-Modul für System Sleep Notifier
// SPDX-License-Identifier: GPL-2.0
//
#include <linux/module.h>
#include <linux/suspend.h>
#ifdef CONFIG_PM_SLEEP
static int foo_notifier( struct notifier_block *nb,
unsigned long action, void *data )
{
char *message;
switch( action ) {
case PM_HIBERNATION_PREPARE:
message = "Going to hibernate"; break;
case PM_POST_HIBERNATION:
message = "Hibernation finished"; break;
case PM_SUSPEND_PREPARE:
message = "Going to suspend the system"; break;
case PM_POST_SUSPEND:
message = "Suspend finished"; break;
case PM_RESTORE_PREPARE:
message = "Going to restore a saved image"; break;
case PM_POST_RESTORE:
message = "Restore finished"; break;
default:
message = "unknown PM action"; break;
}
printk("foo_notifier( %ld ) = \"%s\"\n", action, message );
return 0;
}
static struct notifier_block foo_notifier_nb = {
.notifier_call = foo_notifier,
.priority = 0
};
#endif
static int __init foo_init( void ) {
register_pm_notifier( &foo_notifier_nb );
return 0;
}
static void __exit foo_exit( void ) {
unregister_pm_notifier( &foo_notifier_nb );
return;
}
module_init( foo_init );
module_exit( foo_exit );
MODULE_LICENSE( "GPL v2" );
Praxis
Davon abgesehen lässt sich das erzwungene Stromsparen für die Mehrheit der Geräte recht einfach implementieren. Im günstigsten Fall kann man sogar die Callbacks des Runtime-PM wiederverwerten und benötigt die übrigen Callbacks nur für komplexere Szenarien. Das prinzipielle Vorgehen, also das Verknüpfen der Callbacks mit dem Geräteobjekt entweder über die dort verlinkte Klasse, den verlinkten Bus oder den verlinkten Gerätetyp, hat bereits die letzte Kern-Technik-Folge [2] demonstriert.
System Sleep lässt sich wie das Runtime-Power-Management über das Sys-Filesystem aktivieren und damit austesten. Da sich bei System Sleep das komplette System schlafen legt, greift man dazu nicht auf gerätespezifische Dateien zurück, sondern auf globale Sys-Files im Verzeichnis »/sys/power/«. Ein »cat /sys/power/state« offenbart, welche Suspend-Verfahren der aktuelle Kernel zur Verfügung stellt. Per Echo-Kommando lässt sich dann eines der dort gelisteten Verfahren ausführen. Der Befehl »echo mem > /sys/power/state« versetzt beispielsweise das System in einen Speicherschlaf (Suspend-to-RAM).
Ende gut
Das sanfte Drücken einer Taste auf dem Keyboard oder des Ein-/Ausschaltknopfs katapultiert Linux wieder zurück zu den Lebenden. Es schnurrt typischerweise weiter, als wäre nichts gewesen. Die Zeiten, in denen der Linux-Nutzer auf das Zuklappen seines Notebooks ohne vorhergehenden Shutdown verzichten musste, sind längst passé. Allein bei exotischer Hardware oder verquerer Systemkonfiguration zickt das System noch ab und zu herum. Es gibt also keine Ausrede mehr, Linux nicht für die verdiente Kaffeepause stillzulegen. (jlu)
Die Autoren
Eva-Katharina Kunst ist seit den Anfängen von Linux ein Fan von Open Source. Jürgen Quade lehrt als Professor an der Hochschule Niederrhein und führt auch für Unternehmen Schulungen zu den Themen Treiberprogrammierung und Embedded Linux durch.
Infos
-
System Sleep States, Linux-Kernel-Dokumentation: https://www.kernel.org/doc/html/v5.2/admin-guide/pm/sleep-states.html
-
Kern-Technik, Folge 106: Eva-Katharina Kunst, Jürgen Quade, “Kern-Technik”, LM 11/2019, S. 82, https://www.linux-magazin.de/43365
-
Device Power Management Basics, Linux-Kernel-Dokumentation: https://www.kernel.org/doc/html/v5.2/driver-api/pm/devices.html
-
“Add ‘Hibernate’ Option in Power Menu in Ubuntu 18.04”: http://ubuntuhandbook.org/index.php/2018/05/add-hibernate-option-ubuntu-18-04/





