가상 A/B 개요

Android에는 A/B(원활한) 업데이트와 A/B가 아닌 업데이트의 두 가지 업데이트 메커니즘이 있습니다. 코드 복잡성을 줄이고 업데이트 프로세스를 향상시키기 위해 Android 11에서는 가상 A/B를 통해 두 가지 메커니즘을 통합하여 최소화된 스토리지 비용으로 모든 기기에 원활한 업데이트를 제공합니다. Android 12는 가상 A/B 압축 옵션을 제공하여 스냅샷 파티션을 압축합니다. Android 11 및 Android 12 모두에서 다음이 적용됩니다.

  • 가상 A/B 업데이트는 A/B 업데이트처럼 매끄 럽습니다. 가상 A/B 업데이트는 장치가 오프라인 상태이고 사용할 수 없는 시간을 최소화합니다.
  • 가상 A/B 업데이트는 롤백 할 수 있습니다. 새 OS가 부팅되지 않으면 장치가 자동으로 이전 버전으로 롤백됩니다.
  • 가상 A/B 업데이트는 부트로더에서 사용하는 파티션만 복제하여 최소한의 추가 공간 을 사용합니다. 다른 업데이트 가능한 파티션은 스냅샷 으로 생성됩니다.

배경 및 용어

이 섹션에서는 가상 A/B를 지원하는 기술과 용어를 정의합니다.

장치 매퍼

Device-mapper는 Android에서 자주 사용되는 Linux 가상 블록 계층입니다. 동적 파티션 을 사용하면 /system 과 같은 파티션은 계층화된 장치의 스택입니다.

  • 스택 맨 아래에는 물리적 슈퍼 파티션이 있습니다(예: /dev/block/by-name/super ).
  • 중간에는 슈퍼 파티션의 어떤 블록이 주어진 파티션을 형성하는지 지정하는 dm-linear 장치가 있습니다. 이것은 A/B 장치에서 /dev/block/mapper/system_[a|b] 로 나타나거나 A/B가 아닌 장치에서 /dev/block/mapper/system 으로 나타납니다.
  • 상단에는 검증된 파티션용으로 생성된 dm-verity 장치가 있습니다. 이 장치는 dm-linear 장치의 블록이 올바르게 서명되었는지 확인합니다. /dev/block/mapper/system-verity 로 표시되며 /system 마운트 지점의 소스입니다.

그림 1은 /system 마운트 지점 아래의 스택이 어떻게 생겼는지 보여줍니다.

Partition stacking underneath system

그림 1. /system 마운트 지점 아래에 쌓기

dm-스냅샷

가상 A/B는 dm-snapshot , 저장 장치의 상태를 스냅샷하기 위한 장치 매퍼 모듈에 의존합니다. dm-snapshot 을 사용할 때 4개의 장치가 작동합니다.

  • 기본 장치는 스냅샷이 생성된 장치입니다. 이 페이지에서 기본 장치는 항상 시스템 또는 공급업체와 같은 동적 파티션입니다.
  • 기본 장치에 대한 변경 사항을 기록하기 위한 COW( Copy-On-Write ) 장치. 모든 크기가 될 수 있지만 기본 장치에 대한 모든 변경 사항을 수용할 수 있을 만큼 충분히 커야 합니다.
  • 스냅샷 장치는 snapshot 대상을 사용하여 생성됩니다. 스냅샷 장치에 대한 쓰기는 COW 장치에 기록됩니다. 스냅샷 장치에서 읽기는 액세스 중인 데이터가 스냅샷에 의해 변경되었는지 여부에 따라 기본 장치 또는 COW 장치에서 읽습니다.
  • 원본 장치는 snapshot-origin 대상을 사용하여 생성됩니다. 기본 장치에서 직접 읽은 원본 장치로 읽습니다. 원본 장치에 쓰기는 기본 장치에 직접 쓰지만 원본 데이터는 COW 장치에 쓰기를 통해 백업됩니다.

Device mapping for dm-snapshot

그림 2. dm-snapshot에 대한 장치 매핑

압축된 스냅샷

Android 12에서는 /data 파티션의 공간 요구 사항이 높을 수 있으므로 빌드에서 압축된 스냅샷을 활성화하여 /data 파티션의 더 높은 공간 요구 사항을 해결할 수 있습니다.

가상 A/B 압축 스냅샷은 Android 12에서 사용할 수 있는 두 가지 새로운 구성 요소를 기반으로 합니다.

  • dm-user , 사용자 공간이 블록 장치를 구현할 수 있도록 하는 FUSE와 유사한 커널 모듈입니다.
  • snapuserd , 새로운 스냅샷 형식을 구현하는 사용자 공간 데몬.

이러한 구성 요소는 압축을 가능하게 합니다. 압축된 스냅샷 기능을 구현하는 데 필요한 다른 변경 사항은 다음 섹션에 나와 있습니다. 압축된 스냅샷용 COW 형식 , dm-userSnapuserd .

압축된 스냅샷의 COW 형식

Android 12에서 압축된 스냅샷은 새로운 COW 형식을 사용합니다. 압축되지 않은 스냅샷에 사용되는 커널의 기본 제공 형식과 유사하게 압축된 스냅샷의 COW 형식에는 메타데이터와 데이터 섹션이 번갈아 가며 있습니다. 원래 형식의 메타데이터는 "바꾸기" 작업에만 허용됩니다. 기본 이미지의 블록 X 를 스냅샷의 블록 Y 의 내용으로 바꿉니다. 압축된 스냅샷 COW 형식은 표현이 더 풍부하고 세 가지 작업을 지원합니다.

  • 복사 - 기본 장치의 블록 X 를 기본 장치의 블록 Y 로 교체해야 합니다.
  • 교체 - 기본 장치의 블록 X 를 스냅샷의 블록 Y 의 내용으로 교체해야 합니다. 이러한 각 블록은 gz 압축됩니다.
  • 0 - 기본 장치의 블록 X 는 모두 0으로 대체되어야 합니다.

전체 OTA 업데이트는 교체0 작업으로만 구성됩니다. 증분 OTA 업데이트에는 복사 작업이 추가로 포함될 수 있습니다.

Android 12의 dm-user

dm-user 커널 모듈을 사용하면 userspace 에서 장치 매퍼 블록 장치를 구현할 수 있습니다. dm-user 테이블 항목은 /dev/dm-user/<control-name> 아래에 기타 장치를 생성합니다. userspace 프로세스는 장치를 폴링하여 커널에서 읽기 및 쓰기 요청을 수신할 수 있습니다. 각 요청에는 채우거나(읽기용) 전파할(쓰기용) 사용자 공간에 대한 관련 버퍼가 있습니다.

dm-user 커널 모듈은 업스트림 kernel.org 코드 베이스의 일부가 아닌 커널에 대한 새로운 사용자 표시 인터페이스를 제공합니다. 그때까지 Google은 Android에서 dm-user 인터페이스를 수정할 수 있는 권한을 보유합니다.

스냅유저드

dm-user 에 대한 snapuserd 사용자 공간 구성 요소는 가상 A/B 압축을 구현합니다.

압축되지 않은 버전의 Virtual A/B(Android 11 이하 또는 압축된 스냅샷 옵션이 없는 Android 12)에서 COW 장치는 원시 파일입니다. 압축이 활성화되면 COW는 snapuserd 데몬의 인스턴스에 연결된 dm-user 장치로 대신 작동합니다.

커널은 새로운 COW 형식을 사용하지 않습니다. 따라서 snapuserd 구성 요소는 Android COW 형식과 커널의 내장 형식 간에 요청을 변환합니다.

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

그림 3. Android와 커널 COW 형식 간의 변환기로서의 snapuserd의 흐름도

이 변환 및 압축 해제는 디스크에서 발생하지 않습니다. snapuserd 구성 요소는 커널에서 발생하는 COW 읽기 및 쓰기를 가로채서 Android COW 형식을 사용하여 구현합니다.

가상 A/B 압축 프로세스

이 섹션에서는 가상 A/B 압축에 사용되는 프로세스(메타데이터 읽기, 병합 및 초기화 전환 수행)에 대한 세부 정보를 제공합니다.

메타데이터 읽기

메타데이터는 snapuserd 데몬에 의해 구성됩니다. 메타데이터는 주로 병합할 섹터를 나타내는 2개의 ID(각각 8바이트)의 매핑입니다. dm-snapshot 에서는 disk_exception 이라고 합니다.

struct disk_exception {
    uint64_t old_chunk;
    uint64_t new_chunk;
};

디스크 예외는 데이터의 이전 청크가 새 청크로 대체될 때 사용됩니다.

Snapuserd 데몬은 COW 라이브러리를 통해 내부 COW 파일을 읽고 COW 파일에 있는 각 COW 작업에 대한 메타데이터를 구성합니다.

메타데이터 읽기는 dm- dm- snapshot 장치가 생성될 때 커널의 dm-snapshot 에서 시작됩니다.

아래 그림은 메타데이터 구성을 위한 IO 경로에 대한 시퀀스 다이어그램을 제공합니다.

Sequence diagram, IO path for metadata construction

그림 4. 메타데이터 구성에서 IO 경로의 시퀀스 흐름

병합

부팅 프로세스가 완료되면 업데이트 엔진은 슬롯을 부팅 성공으로 표시하고 dm-snapshot 대상을 dm-snapshot-merge 대상으로 전환하여 병합을 시작합니다.

dm-snapshot 은 메타데이터를 살펴보고 각 디스크 예외에 대해 병합 IO를 시작합니다. 병합 IO 경로에 대한 높은 수준의 개요는 아래와 같습니다.

Merge IO path

그림 5. IO 경로 병합 개요

병합 프로세스 중에 장치를 재부팅하면 다음 재부팅 시 병합이 재개되고 병합이 완료됩니다.

전환 초기화

압축된 스냅샷으로 부팅할 때 첫 번째 단계 초기화는 파티션을 마운트하기 위해 snapuserd 를 시작해야 합니다. 이것은 문제를 제기합니다. sepolicy 가 로드되고 시행될 때 snapuserd 는 잘못된 컨텍스트에 놓이고 selinux 거부와 함께 읽기 요청이 실패합니다.

이 문제를 해결하기 위해 snapuserd 는 다음과 같이 init 와 함께 잠금 단계로 전환합니다.

  1. 1단계 init 는 램디스크에서 snapuserd 를 시작하고 열린 파일 설명자를 환경 변수에 저장합니다.
  2. 첫 번째 단계 init 는 루트 파일 시스템을 시스템 파티션으로 전환한 다음 init 의 시스템 복사본을 실행합니다.
  3. init 의 시스템 복사본은 결합된 sepolicy를 문자열로 읽습니다.
  4. Init 는 모든 ext4 지원 페이지에서 mlock() 을 호출합니다. 그런 다음 스냅샷 장치에 대한 모든 장치 매퍼 테이블을 비활성화하고 snapuserd 를 중지합니다. 그 후에는 교착 상태가 발생하므로 파티션에서 읽는 것이 금지됩니다.
  5. snapuserd 의 ramdisk 복사본에 대한 열린 설명자를 사용하여 init 는 올바른 selinux 컨텍스트로 데몬을 다시 시작합니다. 스냅샷 장치에 대한 장치 매퍼 테이블이 다시 활성화됩니다.
  6. 초기화는 munlockall() 을 호출합니다. IO를 다시 수행하는 것이 안전합니다.

공간 사용

다음 표는 Pixel의 OS 및 OTA 크기를 사용하는 다양한 OTA 메커니즘에 대한 공간 사용량을 비교한 것입니다.

크기 영향 비 A/B A/B 가상 A/B 가상 A/B(압축)
원래 공장 이미지 4.5GB 슈퍼(3.8G 이미지 + 7억 예약) 1 9GB 슈퍼(3.8G + 700M 예약, 슬롯 2개용) 4.5GB 슈퍼(3.8G 이미지 + 7억 예약) 4.5GB 슈퍼(3.8G 이미지 + 7억 예약)
기타 정적 파티션 /은닉처 없음 없음 없음
OTA 중 추가 저장 공간(OTA 적용 후 반환되는 공간) /data의 1.4GB 0 /data의 3.8GB 2 /data의 2.1GB 2
OTA 적용에 필요한 총 스토리지 5.9GB 3 (수퍼 및 데이터) 9GB(슈퍼) 8.3GB 3 (수퍼 및 데이터) 6.6GB 3 (수퍼 및 데이터)

1 픽셀 매핑을 기반으로 가정된 레이아웃을 나타냅니다.

2 새 시스템 이미지가 원본과 동일한 크기라고 가정합니다.

3 공간 요구 사항은 재부팅할 때까지 일시적입니다.

가상 A/B를 구현하거나 압축된 스냅샷 기능을 사용하려면 가상 A/B 구현 을 참조하십시오.