Android分区表深度解析:GPT、各分区作用与布局实战

本文以现代 Android 设备(GPT 分区)为例,详细讲解 Android 设备上每一个分区的作用、布局结构以及如何通过工具读取/修改分区表。

一、为什么要了解分区?

Android 设备底层是一颗 eMMC / UFS 存储芯片,它就像电脑硬盘一样被划分为多个独立区域,每个区域承担不同职责:

  • boot 装的是 Kernel + ramdisk
  • system 装的是 Android 框架代码
  • userdata 装的是用户的所有应用和数据
  • recovery 装的是恢复模式镜像

如果你没搞清楚分区结构,刷错地方轻则变砖,重则永久损坏。


二、MBR vs GPT

早期 Android 设备使用 MBR(Master Boot Record),最多只能划 4 个主分区,且只支持 2TB 以下设备。现代 Android 设备几乎全部转向 GPT (GUID Partition Table)。

GPT 结构

复制代码
偏移 0          : Protective MBR (兼容老工具)
偏移 1 sector   : GPT Header (主头)
偏移 2~33 sector: Partition Entries (128 项 × 128 字节)
...实际分区数据...
末尾倒数 33 sector: Backup Partition Entries
末尾倒数 1 sector : Backup GPT Header

GPT Header 结构(C 描述)

c 复制代码
struct gpt_header {
    uint8_t  signature[8];          // "EFI PART"
    uint32_t revision;              // 0x00010000
    uint32_t header_size;           // 通常 92
    uint32_t header_crc32;          // 头部 CRC
    uint32_t reserved;
    uint64_t current_lba;           // 当前 header 所在 LBA
    uint64_t backup_lba;            // 备份 header 所在 LBA
    uint64_t first_usable_lba;      // 第一个可用 LBA
    uint64_t last_usable_lba;       // 最后一个可用 LBA
    uint8_t  disk_guid[16];         // 磁盘 GUID
    uint64_t partition_entry_lba;   // 分区表起始 LBA
    uint32_t num_partition_entries; // 通常 128
    uint32_t partition_entry_size;  // 通常 128
    uint32_t partition_entries_crc32;
} __attribute__((packed));

Partition Entry 结构

c 复制代码
struct gpt_entry {
    uint8_t  partition_type_guid[16];   // 分区类型 GUID
    uint8_t  unique_partition_guid[16]; // 分区唯一 GUID
    uint64_t starting_lba;              // 起始 LBA
    uint64_t ending_lba;                // 结束 LBA
    uint64_t attributes;                // 属性
    uint16_t partition_name[36];        // UTF-16 分区名 (最多36字符)
} __attribute__((packed));

三、典型 Android 设备的分区列表

以一台高通平台旗舰机为例(2023年款,使用 Dynamic Partition + A/B):

复制代码
分区名             | 大小      | 作用
-------------------|----------|----------------------------------
xbl_a / xbl_b      | 3.5 MB   | eXtensible Bootloader (BootROM 之后的固件)
xbl_config_a/b     | 128 KB   | xbl 配置
abl_a / abl_b      | 2 MB     | Android Boot Loader (基于 UEFI 的 LK)
aop_a / aop_b      | 256 KB   | Always-On Processor 固件
tz_a / tz_b        | 4 MB     | TrustZone 镜像 (TEE 内核)
hyp_a / hyp_b      | 256 KB   | Hypervisor 镜像
modem_a / modem_b  | 130 MB   | 基带固件
dsp_a / dsp_b      | 8 MB     | DSP 固件
boot_a / boot_b    | 100 MB   | Linux Kernel + Generic Ramdisk
init_boot_a/b      | 8 MB     | Init 阶段 ramdisk (Android 13+)
vendor_boot_a/b    | 32 MB    | Vendor 的 ramdisk + DTB
recovery (旧)      | 64 MB    | Recovery 镜像 (A/B 设备已无独立分区)
dtbo_a / dtbo_b    | 8 MB     | Device Tree Blob Overlay
vbmeta_a / vbmeta_b| 64 KB    | AVB 校验元数据
super              | 9 GB     | Dynamic Partition (含 system, vendor, product 等)
metadata           | 16 MB    | 加密元数据
misc               | 1 MB     | Bootloader Control Block (BCB)
persist            | 32 MB    | 厂商持久化数据
userdata           | 剩余空间  | 用户数据

四、关键分区详解

1. boot 分区 --- Android Boot Image

boot 分区里装的是 Android Boot Image,有自己的头部格式:

c 复制代码
// Android Boot Image v3 (Android 11+)
struct boot_img_hdr_v3 {
    uint8_t  magic[8];              // "ANDROID!"
    uint32_t kernel_size;
    uint32_t ramdisk_size;
    uint32_t os_version;
    uint32_t header_size;
    uint32_t reserved[4];
    uint32_t header_version;        // 3
    char     cmdline[1536];
};

/*
 * 布局:
 *   +-----------------+ 0
 *   |  boot header    | 4096 字节
 *   +-----------------+
 *   |     kernel      | n 个 page,n = (kernel_size+4095)/4096
 *   +-----------------+
 *   |     ramdisk     | m 个 page
 *   +-----------------+
 */

可以使用 mkbootimg 工具打包:

bash 复制代码
mkbootimg \
    --kernel out/Image.gz \
    --ramdisk out/ramdisk.cpio.gz \
    --header_version 3 \
    --os_version 13.0.0 \
    --os_patch_level 2024-01 \
    -o boot.img

2. super 分区 --- Dynamic Partition

Android 10 引入 Dynamic Partition ,把 system / vendor / product / system_ext 都合并到一个 super 分区里,内部用 LVM 类似的元数据来管理。

好处是:OEM 不用提前给每个分区分死固定大小,可以动态调整。

复制代码
super 分区
+--------------------------------------+
|  super metadata (geometry + tables)  |  4 KB × 2 (主+备份)
+--------------------------------------+
|  system_a (3 GB)                     |
+--------------------------------------+
|  vendor_a (500 MB)                   |
+--------------------------------------+
|  product_a (1 GB)                    |
+--------------------------------------+
|  system_ext_a (200 MB)               |
+--------------------------------------+
|  ... b slot ...                      |
+--------------------------------------+

查看 super 中的逻辑分区:

bash 复制代码
adb shell su -c "lpdump /dev/block/by-name/super"

输出示例:

复制代码
Slot 0:
Metadata version: 10.2
Metadata size: 4096 bytes
Metadata max size: 65536 bytes
Metadata slot count: 3
Partition table:
------------------------
  Name: system_a
  Group: main_a
  Attributes: readonly
  Extents:
    0 .. 6291455 linear super 2048
  Size: 3221225472
------------------------
  Name: vendor_a
  ...

3. misc 分区 --- Bootloader 通信

misc 是 bootloader 与 Android 系统通信的关键分区,结构定义:

c 复制代码
// bootable/recovery/bootloader_message/include/bootloader_message/bootloader_message.h
struct bootloader_message {
    char command[32];               // "boot-recovery" / "bootonce-bootloader"
    char status[32];
    char recovery[768];             // recovery 命令
    char stage[32];                 // 多阶段恢复
    char reserved[1184];
};

写入 misc 来触发 recovery:

c 复制代码
#include <stdio.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>

int set_bootloader_message(const char *cmd) {
    struct bootloader_message bcb = {0};
    strncpy(bcb.command, cmd, sizeof(bcb.command)-1);

    int fd = open("/dev/block/by-name/misc", O_WRONLY);
    if (fd < 0) return -1;

    ssize_t n = write(fd, &bcb, sizeof(bcb));
    fsync(fd);
    close(fd);
    return (n == sizeof(bcb)) ? 0 : -1;
}

int main(void) {
    return set_bootloader_message("boot-recovery");
}

4. vbmeta 分区 --- AVB 元数据

vbmeta 存放 Android Verified Boot 的签名链,验证 boot/system/vendor 等分区是否被篡改。

复制代码
vbmeta 结构:
+------------------------+
| AvbVBMetaImageHeader   | 256 字节
+------------------------+
| Authentication Data    | 公钥签名 (RSA / Ed25519)
+------------------------+
| Auxiliary Data         | 含 hash descriptor / chain partition / property
+------------------------+

每个被保护的分区(如 boot)的 hash descriptor 描述了它的预期 sha256 值,Bootloader 在启动时会逐一校验。

5. metadata 分区

存放 File-Based Encryption (FBE) 的加密元数据,在 userdata 解密前就要被访问。


五、读取并打印分区表(C 实现)

下面是一个简单的 C 程序,从块设备读取 GPT 并打印所有分区:

c 复制代码
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>

#define LBA_SIZE 4096   // UFS 通常 4K,eMMC 512B

struct gpt_header {
    uint8_t  signature[8];
    uint32_t revision;
    uint32_t header_size;
    uint32_t header_crc32;
    uint32_t reserved;
    uint64_t current_lba;
    uint64_t backup_lba;
    uint64_t first_usable_lba;
    uint64_t last_usable_lba;
    uint8_t  disk_guid[16];
    uint64_t partition_entry_lba;
    uint32_t num_partition_entries;
    uint32_t partition_entry_size;
    uint32_t partition_entries_crc32;
} __attribute__((packed));

struct gpt_entry {
    uint8_t  partition_type_guid[16];
    uint8_t  unique_partition_guid[16];
    uint64_t starting_lba;
    uint64_t ending_lba;
    uint64_t attributes;
    uint16_t partition_name[36];
} __attribute__((packed));

static void utf16_to_ascii(const uint16_t *u16, char *out, size_t n) {
    for (size_t i = 0; i < n; i++) {
        out[i] = (u16[i] < 128) ? (char)u16[i] : '?';
        if (u16[i] == 0) break;
    }
    out[n-1] = 0;
}

int main(int argc, char *argv[]) {
    if (argc < 2) {
        fprintf(stderr, "Usage: %s /dev/block/sdaX\n", argv[0]);
        return 1;
    }

    int fd = open(argv[1], O_RDONLY);
    if (fd < 0) { perror("open"); return 1; }

    // 跳过 protective MBR,读 GPT header
    struct gpt_header hdr;
    lseek(fd, LBA_SIZE * 1, SEEK_SET);
    if (read(fd, &hdr, sizeof(hdr)) != sizeof(hdr)) {
        perror("read header");
        return 1;
    }

    if (memcmp(hdr.signature, "EFI PART", 8) != 0) {
        fprintf(stderr, "Not a GPT disk\n");
        return 1;
    }

    printf("GPT found: %u partition slots, entry size %u\n",
           hdr.num_partition_entries, hdr.partition_entry_size);

    // 读取所有 partition entry
    size_t total = (size_t)hdr.num_partition_entries * hdr.partition_entry_size;
    uint8_t *buf = malloc(total);
    lseek(fd, LBA_SIZE * hdr.partition_entry_lba, SEEK_SET);
    if (read(fd, buf, total) != (ssize_t)total) {
        perror("read entries");
        return 1;
    }

    printf("\n%-3s %-20s %14s %14s\n", "No.", "Name", "Start (KB)", "Size (KB)");
    for (uint32_t i = 0; i < hdr.num_partition_entries; i++) {
        struct gpt_entry *e = (struct gpt_entry *)(buf + i * hdr.partition_entry_size);
        // 全 0 表示未使用
        int empty = 1;
        for (int j = 0; j < 16; j++) if (e->partition_type_guid[j]) { empty = 0; break; }
        if (empty) continue;

        char name[40];
        utf16_to_ascii(e->partition_name, name, sizeof(name));
        uint64_t size = (e->ending_lba - e->starting_lba + 1) * LBA_SIZE / 1024;
        uint64_t start = e->starting_lba * LBA_SIZE / 1024;
        printf("%-3u %-20s %14lu %14lu\n", i, name, start, size);
    }

    free(buf);
    close(fd);
    return 0;
}

编译并运行(需要 root):

bash 复制代码
aarch64-linux-android-gcc gpt_dump.c -o gpt_dump
adb push gpt_dump /data/local/tmp/
adb shell su -c "/data/local/tmp/gpt_dump /dev/block/sda"

六、Android 上查看分区的实用命令

bash 复制代码
# 列出所有 by-name 分区(最常用)
adb shell ls -l /dev/block/by-name/

# 查看分区大小
adb shell cat /proc/partitions

# 备份某个分区
adb shell su -c "dd if=/dev/block/by-name/boot of=/sdcard/boot.img bs=4M"

# 通过 lpdump 看 super 分区内部
adb shell su -c "lpdump"

# 查看分区表(需要 root + gdisk/sgdisk)
adb shell su -c "sgdisk -p /dev/block/sda"

七、常见的踩坑总结

  1. A/B 设备没有 recovery 分区 :recovery.img 被合并进 boot.img 的 ramdisk 中,通过 androidboot.force_normal_boot=1 或 BCB 切换
  2. dtbo 必须和 kernel 匹配:Device Tree 不匹配的话设备会反复重启
  3. vbmeta 不能随便擦 :擦了之后 AVB 会报错,需要 fastboot --disable-verity --disable-verification flash vbmeta
  4. super 分区刷写要走 fastbootd :fastboot reboot fastboot 进入 userspace fastboot 才能刷 system / vendor

八、总结

掌握分区表是 Android 底层工作的基本功:

  • GPT 是现代设备的标准分区格式,理解 header 和 entry 结构能让你直接读出分区表
  • A/B 设备的双槽分区让 OTA 升级可以无缝完成
  • Dynamic Partition (super) 让分区大小动态可变
  • misc / vbmeta / metadata 等小分区虽然不起眼,但承载关键功能

后续会专门讲 A/B 槽位切换和 AVB 验证,欢迎追更。

相关推荐
应用市场2 小时前
Android Recovery 模式工作原理与定制实战
android
应用市场4 小时前
eMMC 与 UFS 存储原理及在 Android 中的应用
android
随遇丿而安4 小时前
第4周:ImageView 最怕的不是不会显示图片,而是显示得“不对劲”
android
Mart!nHu4 小时前
Android10 添加以太网网络共享功能
android·以太网共享
Resistance丶未来6 小时前
【手把手详细教程】 Trae AI和Vscode~使用第三方中转API配置Claude ,GPT,Gemini等大模型教程
人工智能·gpt
修炼者7 小时前
bitmap和drawable的互相转换
android
卷Java7 小时前
GPT-Image 2隐藏玩法:上传手掌照片,AI一键生成专业手相解读图
人工智能·gpt
美狐美颜SDK开放平台7 小时前
美颜SDK接入流程详解:Android、iOS、鸿蒙兼容方案解析
android·人工智能·ios·华为·harmonyos·美颜sdk·视频美颜sdk
笔夏9 小时前
【安卓学习之FloatingActionButton】按钮太小
android·学习