eMMC 与 UFS 存储原理及在 Android 中的应用

本文深入对比 eMMC 与 UFS 两种 Android 设备主流存储介质的硬件架构、协议差异、关键技术,并通过代码示例展示 Linux 下如何读取存储设备的健康信息。

一、为什么手机需要专门的存储芯片?

PC 用 SSD,服务器用 NVMe,而手机这种空间极度受限的设备,需要把"控制器 + NAND 闪存"封装到一颗 BGA 芯片里,这就是 eMMC 和 UFS 的本质 --- 嵌入式存储

它们和原始 NAND 闪存的区别是:控制器已经集成在芯片内部,主机端只需要标准协议就可以读写,不用去操心 NAND 的坏块管理、磨损均衡、ECC 等繁琐工作。


二、eMMC 详解

1. eMMC 的来历

eMMC(embedded MultiMediaCard)由 JEDEC 标准化,本质上是把 MMC 卡(老旧的存储卡格式)封装到 BGA 中,直接焊在主板上。

历代版本:

版本 顺序读 (MB/s) 顺序写 (MB/s) 总线宽度 频率
4.5 ~140 ~50 8 bit 200 MHz
5.0 ~250 ~90 8 bit 200 MHz
5.1 ~350 ~150 8 bit 200 MHz HS400

2. eMMC 接口信号

复制代码
主机 (Host)               eMMC 设备
   +-----+                +-------+
   |     |--CLK---------> |       |
   |     |                |       |
   |     |<--CMD-------->|       |   (双向命令线)
   |     |                |       |
   |     |<--DAT0~DAT7-->|       |   (8 位数据线)
   |     |                |       |
   |     |--RST_n-------->|       |
   |     |                |       |
   +-----+                +-------+

3. eMMC 工作模式

eMMC 设备有 3 个独立的硬件分区(注意!这里的"分区"是硬件层面的,不是 GPT 分区):

  • Boot Area 1 / 2 --- 各 4MB,启动 BootROM 时从这里读

  • RPMB (Replay Protected Memory Block) --- 防回放保护区,存密钥

  • User Area --- 主要存储区,GPT 在这上面

    +--------+
    | Boot 1 | 4 MB -> BootROM 读这里
    +--------+
    | Boot 2 | 4 MB
    +--------+
    | RPMB | 4 MB -> 防重放,用 HMAC-SHA256 保护
    +--------+
    | |
    | User | 剩下的所有空间
    | Area |
    | |
    +--------+

切换访问区域是通过 CMD6 配置 PARTITION_CONFIG 寄存器实现的。

4. eMMC 命令系统

eMMC 命令以 CMDn 表示,每个命令都是 48 位:

复制代码
[47]   起始位 (0)
[46]   传输方向 (主机->设备 = 1)
[45:40] 命令索引 (CMDn)
[39:8]  参数 (32 位)
[7:1]  CRC7
[0]    结束位 (1)

常用命令:

命令 名称 作用
CMD0 GO_IDLE_STATE 复位设备
CMD1 SEND_OP_COND 询问 OCR
CMD2 ALL_SEND_CID 读取 CID(厂商/型号/序列号)
CMD3 SET_RELATIVE_ADDR 分配 RCA
CMD6 SWITCH 切换 ExtCSD
CMD8 SEND_EXT_CSD 读取 ExtCSD (512 字节)
CMD17 READ_SINGLE_BLOCK 读一个块
CMD18 READ_MULTIPLE 读多个块
CMD24 WRITE_BLOCK 写一个块
CMD25 WRITE_MULTIPLE 写多个块

5. 读取 eMMC 健康度

eMMC 5.0+ 支持通过 ExtCSD 读取使用寿命:

c 复制代码
// ExtCSD offsets (JEDEC JESD84-B51)
#define EXT_CSD_DEVICE_LIFE_TIME_EST_TYP_A   268
#define EXT_CSD_DEVICE_LIFE_TIME_EST_TYP_B   269
#define EXT_CSD_PRE_EOL_INFO                 267

/*
 * Pre EOL Info:
 *   0x01 = Normal       (剩 80%+)
 *   0x02 = Warning      (已用 80%~90% 备用块)
 *   0x03 = Urgent       (备用块基本耗尽)
 *
 * Life Time Est:
 *   0x01 = 0~10%
 *   0x02 = 10~20%
 *   ...
 *   0x0A = 90~100%
 *   0x0B = 已超出预期寿命
 */

在 Linux 上读取 ExtCSD:

bash 复制代码
# 需要 mmc-utils
mmc extcsd read /dev/mmcblk0

# 输出片段:
# eMMC Life Time Estimation A [EXT_CSD_DEVICE_LIFE_TIME_EST_TYP_A]: 0x01
# eMMC Life Time Estimation B [EXT_CSD_DEVICE_LIFE_TIME_EST_TYP_B]: 0x01
# eMMC Pre EOL information [EXT_CSD_PRE_EOL_INFO]: 0x01

三、UFS 详解

1. UFS 的革命性

UFS(Universal Flash Storage)是 JEDEC 推出的下一代标准,它的设计借鉴了 PCIe / SATA 的全双工架构,而 eMMC 是半双工。这带来了根本性的性能提升。

历代版本:

版本 顺序读 (MB/s) 顺序写 (MB/s) 通道数 链路速率
2.0 850 250 1-2 HS-G2 (5.8 Gbps)
2.1 860 255 1-2 HS-G3 (11.6 Gbps)
3.0 2100 410 2 HS-G4 (11.6 Gbps)
3.1 2100 1200 2 HS-G4 (11.6 Gbps)
4.0 4200 2800 2 HS-G5 (23.2 Gbps)

2. UFS 协议栈

UFS 协议是分层的,类似网络七层模型:

复制代码
+---------------------------+
|  Application Layer        |  UFS Command Set (SCSI 子集)
+---------------------------+
|  UTP (Transport Protocol) |  UPIU (UFS Protocol Information Unit)
+---------------------------+
|  UniPro                   |  L1.5 - L4 (类似 InfiniBand)
+---------------------------+
|  M-PHY                    |  物理层,差分对,LVDS
+---------------------------+

3. UFS 物理接口

UFS 使用 M-PHY 差分对,信号完整性远超 eMMC 的单端信号:

复制代码
主机 (Host)                  UFS 设备
   +-----+                  +-------+
   |     |--TX_DP/DN1-----> |       |
   |     |--TX_DP/DN2-----> |       |    (双通道发送)
   |     |                  |       |
   |     |<--RX_DP/DN1----- |       |
   |     |<--RX_DP/DN2----- |       |    (双通道接收)
   |     |                  |       |
   |     |--REF_CLK-------> |       |
   |     |--RESET---------> |       |
   +-----+                  +-------+

关键点:UFS 是全双工(同时收发),而 eMMC 是半双工。

4. UFS 的关键特性

(1) 命令队列 (Command Queue)

UFS 支持最多 32 条命令同时排队,主机不需要等一个命令完成才发下一个。

复制代码
eMMC:  CMD1 -> wait -> CMD2 -> wait -> CMD3 -> ...    (串行)

UFS:   CMD1 ┐
       CMD2 ├-> 设备同时处理 -> 乱序返回
       CMD3 ┘
(2) Multi-LUN

UFS 设备内部可以划分多个逻辑单元(Logical Unit),类似 SAS 硬盘:

  • LUN0 ~ LUN7: 普通存储区
  • B0: Boot LUN A
  • B1: Boot LUN B
  • B2: RPMB

每个 LUN 独立寻址,互不影响。

(3) HPB(Host Performance Booster)

UFS 3.1 新增,把 FTL(Flash Translation Layer)映射表的一部分缓存到主机 DRAM,减少随机读延迟。

复制代码
正常路径:    Host -> UFS Controller -> 查 FTL(SRAM) -> 读 NAND -> 返回数据
HPB 路径:    Host -> 命中 HPB Cache(自带映射) -> UFS 直接读 NAND -> 返回

实测随机读 IOPS 可提升 30~50%。

(4) Write Booster

把一部分 TLC/QLC 闪存当 SLC 用,作为高速写入缓存,需要时再回刷到普通区。

5. UPIU 包结构

c 复制代码
// UFS Protocol Information Unit
struct upiu_header {
    uint8_t transaction_type;       // 0x01 = Command, 0x21 = Response ...
    uint8_t flags;
    uint8_t lun;                    // 目标 LUN
    uint8_t task_tag;               // 命令标签 (0~31)
    uint8_t cmd_set_type;           // 0 = SCSI
    uint8_t query_function;
    uint8_t response;
    uint8_t status;
    uint8_t total_ehs_length;
    uint8_t device_info;
    uint16_t data_segment_length;
};

struct upiu_cmd {
    struct upiu_header header;
    uint32_t expected_data_xfer_length;
    uint8_t  cdb[16];               // SCSI CDB
};

主机发送 SCSI 命令(比如 READ(10))时,把它封装到 UPIU 中,再交给 UniPro 传输。


四、eMMC vs UFS 对比

维度 eMMC UFS
双工模式 半双工 全双工
命令队列 不支持(eMMC5.1有有限支持) 支持 32 命令并发
接口类型 并行(CLK+CMD+8DAT) 差分串行(M-PHY)
信号完整性 一般 优秀
功耗 略高
多逻辑单元 仅 3 个固定分区 多个 LUN
顺序读峰值 ~350 MB/s ~4200 MB/s (UFS 4.0)
随机读 IOPS ~12K ~100K+

五、Android 中的 sysfs 接口

在 Android 系统中,可以通过 sysfs 拿到很多存储信息:

eMMC

bash 复制代码
adb shell

# 厂商 CID
cat /sys/block/mmcblk0/device/cid
# 14010038302042414c00010000000000

# 型号
cat /sys/block/mmcblk0/device/name
# H4G2a

# 厂家 ID
cat /sys/block/mmcblk0/device/manfid
# 0x000015  -> Samsung

# 健康度
cat /sys/block/mmcblk0/device/life_time
# 0x01 0x01

UFS

bash 复制代码
# 厂商
cat /sys/devices/.../host0/target0:0:0/0:0:0:0/vendor
# SAMSUNG

# 型号
cat /sys/devices/.../host0/target0:0:0/0:0:0:0/model
# KLUDG4UHDC-B0E1

# 健康度 (UFS 3.1+ 通过 dQueryDescr 读 Health Descriptor)
cat /sys/class/scsi_host/host0/eh_deadline

六、实战:写一个读取 UFS 健康描述符的程序

通过 ioctl 发送 SCSI Query Request,读取 Health Descriptor。

c 复制代码
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <scsi/sg.h>

// Vendor Specific Query (具体 OPCODE 与平台相关)
#define UFS_QUERY_HEALTH_OPCODE 0xC0

struct ufs_health {
    uint8_t length;
    uint8_t descriptor_type;
    uint8_t pre_eol_info;          // 0x01 / 0x02 / 0x03
    uint8_t device_life_a;          // 0x01 ~ 0x0B
    uint8_t device_life_b;
    uint8_t reserved[27];
} __attribute__((packed));

int read_ufs_health(const char *dev, struct ufs_health *health) {
    int fd = open(dev, O_RDWR);
    if (fd < 0) return -1;

    uint8_t cdb[10] = {
        UFS_QUERY_HEALTH_OPCODE, 0, 0, 0,
        0, 0, 0, 0, sizeof(*health), 0
    };
    uint8_t sense[32];
    sg_io_hdr_t io_hdr = {
        .interface_id    = 'S',
        .cmd_len         = sizeof(cdb),
        .mx_sb_len       = sizeof(sense),
        .dxfer_direction = SG_DXFER_FROM_DEV,
        .dxfer_len       = sizeof(*health),
        .dxferp          = health,
        .cmdp            = cdb,
        .sbp             = sense,
        .timeout         = 5000,
    };

    int ret = ioctl(fd, SG_IO, &io_hdr);
    close(fd);
    return ret;
}

int main(int argc, char **argv) {
    struct ufs_health h = {0};
    if (read_ufs_health(argv[1], &h) == 0) {
        printf("Pre-EOL Info        : 0x%02X\n", h.pre_eol_info);
        printf("Life Time A (10%%/u): 0x%02X\n", h.device_life_a);
        printf("Life Time B (10%%/u): 0x%02X\n", h.device_life_b);
    } else {
        fprintf(stderr, "Failed to read UFS health\n");
    }
    return 0;
}

注:不同厂商的 UFS 健康描述符 OPCODE 可能不同,需查阅平台手册。


七、给开发者的实用经验

  1. 写放大要注意:UFS 的 GC(垃圾回收)发生时,瞬时写性能会跳水。不要把日志疯狂打到磁盘
  2. fsync 的代价:fsync 会触发 FUA(Force Unit Access),延迟从微秒级跳到毫秒级,慎用
  3. 大文件用 fallocate:预分配空间可以减少碎片,提升后续写入速度
  4. trim/discard 要开启:让 FTL 知道哪些块可以擦除,延长寿命
  5. 避开 4KB 对齐问题:UFS 内部页通常是 4KB 或 16KB,小于这个粒度的写会触发 RMW(Read-Modify-Write)

八、总结

eMMC 已经是过去式,UFS 是当下中高端 Android 设备的标配,而 UFS 4.0 / UFS 5.0 还在不断突破带宽天花板。理解它们的硬件架构,在做存储性能优化、定位卡顿问题、甚至选择硬件时都极有价值。

下篇我们继续讲 Android 的 A/B 无缝更新,以及它如何巧妙利用 UFS 的多 LUN 设计。

相关推荐
随遇丿而安1 小时前
第4周:ImageView 最怕的不是不会显示图片,而是显示得“不对劲”
android
Mart!nHu1 小时前
Android10 添加以太网网络共享功能
android·以太网共享
修炼者3 小时前
bitmap和drawable的互相转换
android
美狐美颜SDK开放平台4 小时前
美颜SDK接入流程详解:Android、iOS、鸿蒙兼容方案解析
android·人工智能·ios·华为·harmonyos·美颜sdk·视频美颜sdk
笔夏5 小时前
【安卓学习之FloatingActionButton】按钮太小
android·学习
XD7429716366 小时前
科技早报晚报|2026年5月15日:无摄像头空间感知、Android 设备实验室与视频检索代理,今天更值得跟进的 3 个技术机会
android·科技·音视频·开源项目·边缘ai·开发者工具
应用市场6 小时前
Android Verified Boot 2.0 安全启动原理详解
android·安全
只可远观6 小时前
Android XML命令式和Jetpack Compose声明式UI
android·xml
他是龙5516 小时前
DVWA 靶场深度解析:文件包含 & 文件上传(Low → Impossible)
android