Virtuelle A/B-Übersicht

Android verfügt über zwei Update-Mechanismen: A/B-Updates (seamless) und Nicht-A/B-Updates. Um die Codekomplexität zu reduzieren und den Aktualisierungsprozess zu verbessern, werden in Android 11 die beiden Mechanismen durch virtuelles A/B vereinheitlicht, um nahtlose Aktualisierungen für alle Geräte 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 sind 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 gestartet werden kann, werden die Geräte automatisch auf die vorherige Version zurückgesetzt.
  • Virtuelle A/B-Updates verwenden ein Minimum an zusätzlichem Speicherplatz, indem nur die Partitionen dupliziert werden, die vom Bootloader verwendet werden. Von anderen aktualisierbaren Partitionen wird ein Snapshot erstellt .

Hintergrund und Terminologie

Dieser Abschnitt definiert die Terminologie und beschreibt die Technologie, die virtuelles A/B unterstützt.

Gerätemapper

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

  • Am Ende des Stapels befindet sich die physische Superpartition (z. B. /dev/block/by-name/super ).
  • In der Mitte befindet sich ein dm-linear Gerät, das angibt, welche Blöcke in der Superpartition die angegebene 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.
  • Ganz 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 Einhängepunkts /system .

Abbildung 1 zeigt, wie der Stack unter dem Einhängepunkt /system aussieht.

Partition stacking underneath system

Abbildung 1. Stack unter dem Einhängepunkt /system

dm-Schnappschuss

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

  • Das Basisgerät ist das Gerät, das einen Snapshot erstellt. 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. Es kann beliebig groß sein, muss aber 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. Lesevorgänge vom Snapshot-Gerät lesen entweder vom Basisgerät oder vom COW-Gerät, je nachdem, 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 dem Ursprungsgerät, das direkt vom Basisgerät gelesen wird. Schreibvorgänge auf das Ursprungsgerät schreiben direkt auf das Basisgerät, aber die ursprünglichen Daten werden gesichert, indem auf das COW-Gerät geschrieben wird.

Device mapping for dm-snapshot

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

Komprimierte Schnappschüsse

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

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

  • dm-user , ein Kernel-Modul ähnlich 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 eingebaute Format des Kernels, das für unkomprimierte Snapshots verwendet wird, hat das COW-Format für die komprimierten Snapshots abwechselnde Abschnitte von Metadaten und Daten. Die Metadaten des ursprünglichen Formats sind nur für Ersetzungsvorgänge zulässig: Ersetzen Sie Block X im Basisbild durch den Inhalt von Block Y im Snapshot. Das komprimierte COW-Snapshot-Format ist ausdrucksstärker und unterstützt die folgenden Operationen:

  • 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 alle 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 Nulloperationen . Inkrementelle OTA-Updates können zusätzlich Kopiervorgänge haben.

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 erhalten. Jede Anfrage hat einen zugehörigen Puffer, den der Userspace entweder füllen (für einen Lesevorgang) oder propagieren (für einen Schreibvorgang) kann.

Das dm-user Kernelmodul bietet eine neue, für den Benutzer sichtbare Schnittstelle zum Kernel, 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 Userspace-Komponente snapuserd 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 komprimierte Snapshot-Option) ist das COW-Gerät eine Rohdatei. Wenn die Komprimierung aktiviert ist, fungiert der COW stattdessen als dm-user Gerät, das mit einer Instanz des snapuserd Daemons verbunden ist.

Der Kernel verwendet das neue COW-Format nicht. Die snapuserd Komponente übersetzt also Anfragen zwischen dem Android COW-Format und dem eingebauten 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 erfolgt niemals auf der Festplatte. Die snapuserd Komponente fängt die COW-Lese- und -Schreibvorgänge ab, die im Kernel auftreten, und implementiert sie mithilfe des Android-COW-Formats.

XOR-Komprimierung

Bei Geräten, die mit Android 13 und höher gestartet werden, ermöglicht die standardmäßig aktivierte XOR-Komprimierungsfunktion, dass Userspace-Snapshots XOR-komprimierte Bytes zwischen alten Blöcken und neuen Blöcken speichern. Wenn nur wenige Bytes in einem Block in einem virtuellen A/B-Update geändert werden, verwendet das XOR-Komprimierungsspeicherschema weniger Speicherplatz als das Standardspeicherschema, da Snapshots keine vollen 4K-Bytes speichern. Diese Reduzierung der Snapshot-Größe ist möglich, da 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 aktualisieren, muss die XOR-Komprimierung aktiviert sein. Einzelheiten finden Sie unter XOR-Komprimierung .

Virtuelle A/B-Komprimierungsverfahren

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

Metadaten lesen (Android 12)

Metadaten werden von einem snapuserd Daemon erstellt. Die Metadaten sind in erster Linie eine Abbildung 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 Plattenausnahme 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- dm- snapshot -Gerät erstellt wird.

Die folgende Abbildung zeigt ein Sequenzdiagramm für den IO-Pfad für die Metadatenkonstruktion.

Sequence diagram, IO path for metadata construction

Abbildung 4. Sequenzfluss für den IO-Pfad in der Metadatenkonstruktion

Zusammenführen (Android 12)

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

dm-snapshot durchläuft die Metadaten und initiiert einen Zusammenführungs-E/A für jede Datenträgerausnahme. Eine allgemeine Übersicht über den Zusammenführungs-E/A-Pfad ist unten dargestellt.

Merge IO path

Abbildung 5. Übersicht über Merge-IO-Pfade

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 abgeschlossen.

Device-Mapper-Layering

Bei Geräten, die mit Android 13 und höher gestartet werden, werden die Snapshot- und Snapshot-Zusammenführungsprozesse in der virtuellen A/B-Komprimierung von der snapuserd Userspace-Komponente durchgeführt. Für Geräte, die auf Android 13 und höher aktualisieren, muss diese Funktion aktiviert werden. Einzelheiten finden Sie unter Userspace-Merge .

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

  1. Das Framework stellt die /system Partition von einem dm-verity Gerät bereit, 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-Anforderung 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.

Übergänge einleiten

Beim Booten mit komprimierten Snapshots muss die Init der ersten Stufe snapuserd starten, um Partitionen zu mounten. Dies stellt ein Problem dar: Wenn sepolicy geladen und erzwungen wird, wird snapuserd in den falschen Kontext versetzt, und seine Leseanforderungen schlagen mit selinux-Denials fehl.

Um dies zu beheben, werden snapuserd -Übergänge im Gleichschritt mit init wie folgt ausgeführt:

  1. init der ersten Stufe startet snapuserd von der Ramdisk und speichert einen offenen Dateideskriptor in einer Umgebungsvariablen.
  2. init der ersten Stufe schaltet das Root-Dateisystem auf die Systempartition um 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-unterstützten Seiten auf. Es deaktiviert dann alle Device-Mapper-Tabellen für Snapshot-Geräte und stoppt snapuserd . Danach ist es verboten, von Partitionen zu lesen, da dies zu einem Deadlock führt.
  5. Unter Verwendung des offenen Deskriptors für die Ramdisk-Kopie von snapuserd init den Daemon mit dem korrekten Selinux-Kontext neu. Device-Mapper-Tabellen für Snapshot-Devices werden reaktiviert.
  6. Init ruft munlockall() auf - es ist sicher, IO erneut auszuführen.

Platznutzung

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

Einfluss auf die Größe Nicht-A/B A/B Virtuelles A/B Virtuelles A/B (komprimiert)
Originalbild der Fabrik 4,5 GB Super (3,8 GB Bild + 700 MB reserviert) 1 9 GB Super (3,8 G + 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 wird nach Anwendung von OTA zurückgegeben) 1,4 GB auf /data 0 3,8 GB 2 auf /data 2,1 GB 2 auf /data
Für die Anwendung von OTA erforderlicher Gesamtspeicher 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 Speicherplatzbedarf ist vorübergehend bis zum Neustart.

Informationen zum Implementieren von Virtual A/B oder zum Verwenden komprimierter Snapshot-Funktionen finden Sie unter Implementieren von Virtual A/B