Virtuelle A/B-Übersicht

Android verfügt über zwei Update-Mechanismen: A/B-Updates (nahtlos) und Nicht-A/B-Updates. Um die Komplexität des Codes zu reduzieren und den Update-Prozess zu verbessern, werden in Android 11 die beiden Mechanismen durch virtuelles A/B vereinheitlicht, um nahtlose Updates auf allen Geräten mit minimierten Speicherkosten bereitzustellen. Android 12 bietet die Option der virtuellen A/B-Komprimierung, um Snapshot-Partitionen zu komprimieren. Sowohl in Android 11 als auch in Android 12 gilt Folgendes:

  • Virtuelle A/B-Updates erfolgen nahtlos wie A/B-Updates. Virtuelle A/B-Updates minimieren die Zeit, in der ein Gerät offline und unbrauchbar ist.
  • Virtuelle A/B-Updates können rückgängig gemacht werden. Wenn das neue Betriebssystem nicht startet, werden die Geräte automatisch auf die vorherige Version zurückgesetzt.
  • Virtuelle A/B-Updates beanspruchen nur ein Minimum an zusätzlichem Speicherplatz, indem nur die Partitionen dupliziert werden, die vom Bootloader verwendet werden. Andere aktualisierbare Partitionen werden per Snapshot erstellt .

Hintergrund und Terminologie

In diesem Abschnitt wird die Terminologie definiert und die Technologie beschrieben, die virtuelles A/B unterstützt.

Geräte-Mapper

Device-Mapper ist eine virtuelle Linux-Blockschicht, die häufig in Android verwendet wird. Bei dynamischen Partitionen sind Partitionen wie /system ein Stapel geschichteter Geräte:

  • Am unteren Ende des Stapels befindet sich die physische Superpartition (z /dev/block/by-name/super ).
  • In der Mitte befindet sich ein dm-linear Gerät, das angibt, welche Blöcke in der Superpartition die gegebene Partition bilden. Dies erscheint als /dev/block/mapper/system_[a|b] auf einem A/B-Gerät oder /dev/block/mapper/system auf einem Nicht-A/B-Gerät.
  • Oben befindet sich ein dm-verity Gerät, das für verifizierte Partitionen erstellt wurde. Dieses Gerät überprüft, ob Blöcke auf dem dm-linear -Gerät korrekt signiert sind. Es erscheint als /dev/block/mapper/system-verity und ist die Quelle des /system Mountpunkts.

Abbildung 1 zeigt, wie der Stapel unter dem Mountpunkt /system aussieht.

Partition stacking underneath system

Abbildung 1. Stapel unter dem Mountpunkt /system

dm-Schnappschuss

Virtual A/B basiert auf dm-snapshot , einem Device-Mapper-Modul zum Snapshoten des Zustands eines Speichergeräts. Bei Verwendung von dm-snapshot sind vier Geräte im Spiel:

  • Das Basisgerät ist das Gerät, von dem ein Snapshot erstellt wird. Auf dieser Seite ist das Basisgerät immer eine dynamische Partition, z. B. System oder Hersteller.
  • Das COW-Gerät ( Copy-on-Write ) zum Protokollieren von Änderungen am Basisgerät. Die Größe kann beliebig sein, sie muss jedoch groß genug sein, um alle Änderungen am Basisgerät aufzunehmen.
  • Das Snapshot- Gerät wird mithilfe des snapshot Ziels erstellt. Schreibvorgänge auf das Snapshot-Gerät werden auf das COW-Gerät geschrieben. Liest vom Snapshot-Gerät entweder vom Basisgerät oder vom COW-Gerät, abhängig davon, ob die Daten, auf die zugegriffen wird, durch den Snapshot geändert wurden.
  • Das Ursprungsgerät wird mithilfe des snapshot-origin Ziels erstellt. Liest auf das Ursprungsgerät, das direkt vom Basisgerät gelesen wird. Schreibt auf das Ursprungsgerät und schreibt direkt auf das Basisgerät, die Originaldaten werden jedoch durch Schreiben auf das COW-Gerät gesichert.

Device mapping for dm-snapshot

Abbildung 2. Gerätezuordnung für dm-snapshot

Komprimierte Schnappschüsse

Da in Android 12 und höher der Speicherplatzbedarf auf der /data Partition hoch sein kann, können Sie komprimierte Snapshots in Ihrem Build aktivieren, um den höheren Speicherplatzbedarf der /data Partition zu decken.

Virtuelle A/B-komprimierte Snapshots basieren auf den folgenden Komponenten, die in Android 12 und höher verfügbar sind:

  • dm-user , ein Kernelmodul ähnlich wie FUSE, das es dem Userspace ermöglicht, Blockgeräte zu implementieren.
  • snapuserd , ein Userspace-Daemon zur Implementierung eines neuen Snapshot-Formats.

Diese Komponenten ermöglichen die Komprimierung. Die anderen notwendigen Änderungen, die zur Implementierung der Funktionen für komprimierte Snapshots vorgenommen wurden, werden in den nächsten Abschnitten beschrieben: COW-Format für komprimierte Snapshots , dm-user und Snapuserd .

COW-Format für komprimierte Schnappschüsse

In Android 12 und höher verwenden komprimierte Snapshots ein COW-Format. Ähnlich wie das im Kernel integrierte Format für unkomprimierte Snapshots verfügt das COW-Format für die komprimierten Snapshots über abwechselnde Abschnitte mit Metadaten und Daten. Die Metadaten des Originalformats sind nur für Ersetzungsvorgänge zulässig: Ersetzen Sie Block X im Basis-Image durch den Inhalt von Block Y im Snapshot. Das komprimierte COW-Snapshot-Format ist ausdrucksvoller und unterstützt die folgenden Vorgänge:

  • Kopieren : Block X im Basisgerät sollte durch Block Y im Basisgerät ersetzt werden.
  • Ersetzen : Block X im Basisgerät sollte durch den Inhalt von Block Y im Snapshot ersetzt werden. Jeder dieser Blöcke ist gz-komprimiert.
  • Null : Block X im Basisgerät sollte durch ausschließlich Nullen ersetzt werden.
  • XOR : Das COW-Gerät speichert XOR-komprimierte Bytes zwischen Block X und Block Y. (Verfügbar in Android 13 und höher.)

Vollständige OTA-Updates bestehen nur aus Ersetzungs- und Nullvorgängen . Inkrementelle OTA-Updates können zusätzlich Kopiervorgänge beinhalten.

dm-Benutzer in Android 12

Das dm-user-Kernelmodul ermöglicht es userspace Device-Mapper-Blockgeräte zu implementieren. Ein dm-user-Tabelleneintrag erstellt ein sonstiges Gerät unter /dev/dm-user/<control-name> . Ein userspace Prozess kann das Gerät abfragen, um Lese- und Schreibanforderungen vom Kernel zu empfangen. Jeder Anfrage ist ein Puffer zugeordnet, den der Benutzerbereich entweder füllen (für einen Lesevorgang) oder weitergeben (für einen Schreibvorgang) kann.

Das dm-user Kernelmodul stellt eine neue, für den Benutzer sichtbare Schnittstelle zum Kernel bereit, die nicht Teil der Upstream-Codebasis von kernel.org ist. Bis dahin behält sich Google das Recht vor, die dm-user in Android zu ändern.

snapuserd

Die snapuserd Userspace-Komponente für dm-user implementiert die virtuelle A/B-Komprimierung.

In der unkomprimierten Version von Virtual A/B (entweder in Android 11 und niedriger oder in Android 12 ohne die Option für komprimierte Snapshots) ist das COW-Gerät eine Rohdatei. Wenn die Komprimierung aktiviert ist, fungiert das COW stattdessen als dm-user , das mit einer Instanz des snapuserd Daemons verbunden ist.

Der Kernel verwendet nicht das neue COW-Format. Die snapuserd Komponente übersetzt also Anfragen zwischen dem Android COW-Format und dem integrierten Format des Kernels:

Snapuserd component translating requests between Android COW format and kernel built-in format

Abbildung 3. Flussdiagramm von snapuserd als Übersetzer zwischen Android- und Kernel-COW-Formaten

Diese Übersetzung und Dekomprimierung findet nie auf der Festplatte statt. Die snapuserd Komponente fängt die COW-Lese- und Schreibvorgänge im Kernel ab und implementiert sie mithilfe des Android COW-Formats.

XOR-Komprimierung

Bei Geräten, die mit Android 13 und höher starten, ermöglicht die standardmäßig aktivierte XOR-Komprimierungsfunktion Userspace-Snapshots, um XOR-komprimierte Bytes zwischen alten und neuen Blöcken zu speichern. Wenn bei einem Virtual A/B-Update nur wenige Bytes in einem Block geändert werden, verbraucht das XOR-Komprimierungsspeicherschema weniger Speicherplatz als das Standardspeicherschema, da Snapshots nicht die gesamten 4 KB speichern. Diese Reduzierung der Snapshot-Größe ist möglich, weil XOR-Daten viele Nullen enthalten und einfacher zu komprimieren sind als Rohblockdaten. Auf Pixel-Geräten reduziert die XOR-Komprimierung die Snapshot-Größe um 25 % bis 40 %.

Für Geräte, die auf Android 13 und höher aktualisiert werden, muss die XOR-Komprimierung aktiviert sein. Einzelheiten finden Sie unter XOR-Komprimierung .

Virtuelle A/B-Komprimierungsverfahren

Dieser Abschnitt enthält Details zum Virtual A/B-Komprimierungsprozess, der in Android 13 und Android 12 verwendet wird.

Metadaten lesen (Android 12)

Metadaten werden von einem snapuserd Daemon erstellt. Bei den Metadaten handelt es sich in erster Linie um eine Zuordnung von zwei IDs mit jeweils 8 Bytes, die die zusammenzuführenden Sektoren darstellen. In dm-snapshot heißt es disk_exception .

struct disk_exception {
    uint64_t old_chunk;
    uint64_t new_chunk;
};

Eine Festplattenausnahme wird verwendet, wenn ein alter Datenblock durch einen neuen ersetzt wird.

Ein snapuserd Daemon liest die interne COW-Datei über die COW-Bibliothek und erstellt die Metadaten für jede der in der COW-Datei vorhandenen COW-Operationen.

Metadaten-Lesevorgänge werden vom dm-snapshot im Kernel initiiert, wenn das dm- snapshot Gerät erstellt wird.

Die folgende Abbildung zeigt ein Sequenzdiagramm für den E/A-Pfad für die Metadatenkonstruktion.

Sequence diagram, IO path for metadata construction

Abbildung 4. Sequenzfluss für den E/A-Pfad bei der Metadatenkonstruktion

Zusammenführen (Android 12)

Sobald der Startvorgang abgeschlossen ist, markiert die Update-Engine den Steckplatz als Start erfolgreich und initiiert die Zusammenführung, indem sie das dm-snapshot Ziel auf das dm-snapshot-merge Ziel umstellt.

dm-snapshot durchsucht die Metadaten und initiiert für jede Festplattenausnahme einen Merge-IO. Nachfolgend finden Sie eine allgemeine Übersicht über den Merge-IO-Pfad.

Merge IO path

Abbildung 5. Übersicht über den Merge-IO-Pfad

Wenn das Gerät während des Zusammenführungsvorgangs neu gestartet wird, wird die Zusammenführung beim nächsten Neustart fortgesetzt und die Zusammenführung ist abgeschlossen.

Device-Mapper-Layering

Bei Geräten, die mit Android 13 und höher gestartet werden, werden die Snapshot- und Snapshot-Merge-Prozesse bei der Virtual A/B-Komprimierung von der snapuserd Userspace-Komponente ausgeführt. Für Geräte, die auf Android 13 und höher aktualisiert werden, muss diese Funktion aktiviert sein. Einzelheiten finden Sie unter Userspace-Zusammenführung .

Im Folgenden wird der virtuelle A/B-Komprimierungsprozess beschrieben:

  1. Das Framework mountet die /system -Partition von einem dm-verity -Gerät, das auf einem dm-user -Gerät gestapelt ist. Das bedeutet, dass jede E/A vom Root-Dateisystem an dm-user weitergeleitet wird.
  2. dm-user leitet die E/A an den Userspace snapuserd Daemon weiter, der die E/A-Anfrage verarbeitet.
  3. Wenn der Zusammenführungsvorgang abgeschlossen ist, reduziert das Framework dm-verity auf dm-linear ( system_base ) und entfernt dm-user .

Virtueller A/B-Komprimierungsprozess

Abbildung 6. Virtueller A/B-Komprimierungsprozess

Der Snapshot-Zusammenführungsprozess kann unterbrochen werden. Wenn das Gerät während des Zusammenführungsvorgangs neu gestartet wird, wird der Zusammenführungsvorgang nach dem Neustart fortgesetzt.

Init-Übergänge

Beim Booten mit komprimierten Snapshots muss die Init-Initialisierung der ersten Stufe snapuserd starten, um Partitionen bereitzustellen. Dies stellt ein Problem dar: Wenn sepolicy geladen und durchgesetzt wird, wird snapuserd in den falschen Kontext gestellt und seine Leseanforderungen schlagen fehl, was zu Selinux-Ablehnungen führt.

Um dieses Problem zu beheben, snapuserd einen Lockstep-Übergang mit init wie folgt durch:

  1. In der ersten Stufe startet init snapuserd von der Ramdisk und speichert dort einen offenen Dateideskriptor in einer Umgebungsvariablen.
  2. Init der ersten Stufe schaltet das Root-Dateisystem auf die Systempartition init und führt dann die Systemkopie von init aus.
  3. Die Systemkopie von init liest die kombinierte Sepolicy in einen String.
  4. Init ruft mlock() auf allen ext4-gestützten Seiten auf. Anschließend werden alle Device-Mapper-Tabellen für Snapshot-Geräte deaktiviert und snapuserd gestoppt. Danach ist das Lesen von Partitionen verboten, da dies zu einem Deadlock führt.
  5. Unter Verwendung des offenen Deskriptors für die Ramdisk-Kopie von snapuserd startet init den Daemon mit dem richtigen Selinux-Kontext neu. Gerätezuordnungstabellen für Snapshot-Geräte werden erneut aktiviert.
  6. Init ruft munlockall() auf – es ist sicher, IO erneut durchzuführen.

Raumnutzung

Die folgende Tabelle bietet einen Vergleich der Speicherplatznutzung für verschiedene OTA-Mechanismen unter Verwendung der Betriebssystem- und OTA-Größen von Pixel.

Auswirkungen auf die Größe Nicht-A/B A/B Virtuelles A/B Virtuelles A/B (komprimiert)
Originales Werksbild 4,5 GB Super (3,8 GB Bild + 700 MB reserviert) 1 9 GB Super (3,8 GB + 700 MB reserviert, für zwei Steckplätze) 4,5 GB Super (3,8 GB Bild + 700 MB reserviert) 4,5 GB Super (3,8 GB Bild + 700 MB reserviert)
Andere statische Partitionen /Zwischenspeicher Keiner Keiner Keiner
Zusätzlicher Speicher während OTA (Speicherplatz, der nach der Anwendung von OTA zurückgegeben wird) 1,4 GB auf /data 0 3,8 GB 2 auf /data 2,1 GB 2 auf /data
Gesamter Speicherplatz, der für die Anwendung von OTA erforderlich ist 5,9 GB 3 (Super und Daten) 9 GB (Super) 8,3 GB 3 (Super und Daten) 6,6 GB 3 (Super und Daten)

1 Zeigt das angenommene Layout basierend auf der Pixelzuordnung an.

2 Geht davon aus, dass das neue Systemabbild die gleiche Größe wie das Original hat.

3 Der Speicherplatzbedarf ist bis zum Neustart vorübergehend.

Informationen zur Implementierung von Virtual A/B oder zur Verwendung komprimierter Snapshot-Funktionen finden Sie unter Implementieren von Virtual A/B