☰
Android新增分区实战:从bootloader到SELinux的完整避坑指南
2026/10/21 7:33:56 网站建设 项目流程

简介:面向MTK平台Android 9.0及以上系统定制开发者,这份zip包提供了在MT6761上新增FDRAM分区并挂载到根目录的完整实操参考,覆盖分区表修改、文件系统格式化、fstab挂载配置及安全注意事项,并针对Android 9.0 A/B更新与Vold框架给出适配思路。包内共21个文件,以mk、te、csv、py等类型为主,包含构建配置、SELinux策略、分区定义和辅助脚本,另有makefile、rc、c等文件,整体仅133KB,便于快速下载查阅。当前已有682人学习。资源中附有partition.map与partition.bin调整方法、mkfs.f2fs/ext4格式化命令、挂载权限设置示例及多系统分区适配细节,适合有一定内核与文件系统基础的MTK工程师直接参考,可显著缩短分区扩展的开发验证周期。

1. 给 Android 加一个新分区:一次格式化变砖后的复盘

在定制 Android 系统时,“加一个分区再挂到根目录”听起来是最不起眼的需求,实际上它同时踩了 bootloader 分区表、内核设备树、fstab、init 和 SELinux 五道门槛,任何一道漏掉,轻则挂载后目录为空,重则开机直接卡在 logo。这套资源包就是从一次真实变砖中反推出来的完整清单,把新分区 FDRAM 从“在 gpt 里画一块区域”到“开机自动出现在根目录下指定位置”的每一步都打成了可复用的补丁与脚本。适合正在做系统定制、整机 ROM、行业终端 BSP 的工程师,也适合第一次被“分区挂载”需求砸中的入门开发者照着复现。

2. 挂载链路四道关卡:为什么改 fstab 远远不够

2.1 第一关:bootloader 分区表,先在“地基”里画一块地

很多人以为新增分区只改 fstab 就行,这是第一个翻车点。Android 设备的存储布局由 bootloader 阶段的分区表定义,它不只是一张“名字到起始扇区”的映射表,还承担了开机时给内核传递分区参数、配合恢复出厂、OTA 差分升级等职责。改 fstab 等于告诉系统“我要用一块地”,但没人在地上画线,这块地根本不存在。

这套资源包里改的第一个文件就是 bootloader 分区描述文件。常见做法是维护一个静态分区数组,每个分区有名称、起始偏移、大小、标志位:

/* partition_config.c */ static const struct pt_entry partitions[] = { /* 原有分区... */ { .name = "system", .start = 0x00200000, .size = 0x06000000, .flags = PT_SYSTEM, }, /* 新增 FDRAM 分区,紧跟 system 之后 */ { .name = "FDRAM", .start = 0x08000000, .size = 0x02000000, /* 512MB,按实际需求调整 */ .flags = PT_USER, /* 用户数据区,不参与系统校验 */ }, /* 原有分区... */ };

逻辑上并没有多复杂:新分区只是数组里多了一行,但请注意.start是硬编码偏移,意味着它前面的所有分区大小只要动过,这里就得同步算。资源包的做法是不手算,而是在编译时用脚本从前一个分区的start + size自动推导,杜绝“算错偏移导致分区重叠”的隐患。

参数层面,.flags决定分区能不能被普通文件系统挂载、是否纳入 dm-verity 校验、是否允许恢复出厂时格式化。给 FDRAM 标PT_USER意味着它属于用户数据性质,和/data同策略;如果你希望它像system一样只读校验,那就标PT_SYSTEM,但那样的话“往里写数据”的需求就要重新考虑了。我一般建议数据扩展区都按PT_USER处理,省事且符合直觉。

2.2 第二关:内核设备树,给分区一张“身份证”

bootloader 画完地,内核还需要在设备树里见到这个分区,否则 Linux 的块设备层根本不会为它创建/dev/block/...节点。这一步漏掉的典型症状是:fstab 里写了挂载点,开机 log 里报device not found,目录存在但完全没挂载。

设备树里描述的是一块“存储区域”,不是“文件系统”。看资源里给的 dts 片段:

/* 在 boot 设备节点的 partitions 子节点中追加 */ partition@8000000 { label = "FDRAM"; reg = <0x08000000 0x02000000>; /* 起始地址 128MB,大小 512MB */ };

注意reg的两个值必须和 bootloader 分区表完全一致,起始地址和大小都是十六进制字节数。这里最容易犯的错是拿 MB 当字节直接写进去——0x2000000是 32MB,不是 512MB,差了 16 倍。资源包附带了一个check_partition.py脚本,专门读取 bootloader 表和 dts 的reg做交叉比对,数值不一致直接报错退出编译,从流程上掐掉这种低级但致命的错。

设备树节点的名字partition@后缀理论上只要求唯一,但内核的 mmc 驱动会按这个reg注册块设备号,所以它的排序决定了/dev/block/mmcblk0pN的 N。如果新分区插在其他分区中间,后面所有分区的 N 都会后移,fstab 里没有用by-name而是用裸节点名的写法就会全部失效。这套资源包强制建议 fstab 一律用by-name/FDRAM这种符号链接,原因就在这。

2.3 第三关:fstab 与 init 配合,挂载时机比挂载命令更重要

fstab 文件是第二阶段 init 解析的关键输入,它决定了分区在用户空间启动的哪个阶段、以什么方式挂载到哪个目录。但“挂载到根目录”这个需求需要先拆解:不是往/上挂(那是根文件系统本身的位置),而是在根文件系统下的某个专门目录挂载新分区,比如/frdata。目录可以是空的,不影响根文件系统,但你要保证它在挂载动作发生前已经存在。

资源包里提供的 fstab 写法:

# vendor/etc/fstab # 设备节点 挂载点 文件系统类型 挂载标志 其他选项 /dev/block/by-name/FDRAM /frdata ext4 rw,noatime wait,check,fileencryption=software

wait表示 init 会等待这个设备节点出现再继续,避免 boot 设备 probe 慢导致挂载失败;check表示开机时会用 fsck 检查文件系统完整性;fileencryption=software是为 FDRAM 单独开启软件级加密,这是/data之后第二个加密分区时很容易忽略的选项——不加这行,SDC 或 vold 的加密策略会对陌生分区直接跳过,数据裸奔不说,某些 OEM 的恢复出厂流程还会拒绝格式化它。

init.rc 侧的配合则是在挂载时机上。卷管理(vold)的挂载路径是异步的,不适合开机关键路径;FDRAM 如果承载的是日志、缓存、预加载资源这类开机就需要访问的内容,建议走 init 的mount_all:

on fs mount_all /vendor/etc/fstab # 挂载完成后设置属主 chown system system /frdata chmod 0771 /frdata

on fs阶段比on post-fs-data早,意味着 FDRAM 在 zygote 启动前就已就位,App 一开机就能读写。代价是这一阶段挂载失败会直接触发 panic(翻车现场),所以资源包里放了一个开关宏——调试期把该行挪到on post-fs-data,降级为“挂不上也继续开机”,等稳定再挪回关键路径。

2.4 第四关:SELinux 策略,让新分区不再“有权限却没权限”

分区挂上了,目录存在,df也看得到,但一写入就Permission denied——这是 SELinux 在挡路,也是新手最觉得“玄学”的一关。Android 的用户空间大量进程跑在受限域里,vold、init、system_server对非标准分区的访问默认都是拒绝的。

资源包里加入了三个层面的 SELinux 补丁:

# fdr.am.te type fdr_data_file, file_type, data_file_type; typeattribute fdr_data_file core_data_file_type;

第一个.te声明了新的文件类型标签。然后是file_contexts把实际目录映射到标签:

/frdata(/.*)? u:object_r:fdr_data_file:s0

缺少这条,目录还是default_t类型,带core_data_file_type属性的进程照样写不进去。最后是授权规则,资源包按“最小权限”给了init和vold的标签声明,不直接给system_server开全权限,避免扩大攻击面:

allow init fdr_data_file:dir { create read write open search setattr }; allow init fdr_data_file:file { create read write open setattr }; allow vold fdr_data_file:dir { read write search create mounton };

四条规则之外,别忘了seapp_contexts可能需要为新分区上运行的应用进程单独指派 domain。如果是系统级分区,不做这步也能跑;如果某个第三方 App 要直接读写 FDRAM,那它所在的 domain 必须显式allow,否则应用层表现为“文件管理器能看到目录,但拷贝文件永远失败”。

3. 动手落地:把 FDRAM 变更合入现有工程

3.1 先跑检查脚本,确认分区表中没有旧定义

拿到资源包的第一件事不是改代码,而是确认你的基线工程里不存在同名分区。某些厂商 BSP 里 FDRAM 或类似名字已被占用于隐藏区域,硬加数组项会导致 bootloader 解析直接报partition duplicated,卡死在 preloader 阶段。

python3 tool/check_dup_partition.py --config bootloader/pt_config.c

脚本会扫描分区名并输出冲突项。如果返回零,进入下一步;如果有同名分区,先在原位置把名字改为FDRAM_BACKUP这类临时名,不要直接删——有些分区是 OTA 增量升级的依赖项,删了会破坏后续升级链。

3.2 统一修改三处描述文件,保持字节级一致

最稳的顺序是先改 bootloader 分区表,再改 dts,最后改 fstab。三处的“起始地址/大小”必须完全一致,资源包里用一个part_info.json作为唯一数据源:

{ "name": "FDRAM", "start_mb": 2048, "size_mb": 512, "filesystem": "ext4", "mount_point": "/frdata" }

之后执行python3 gen_partitions.py,它会同时生成 C 结构体片段、dts 片段和 fstab 片段,不给你任何手改漂移的机会。这个脚本的逻辑看起来简单,但它把“一致性”从人的纪律变成了流程约束,强烈建议沿用。

3.3 生成文件系统镜像并固化到用户数据区

FDRAM 的初始内容有两种来源:空分区(第一次刷机后由 init 格式化)和预置镜像(出厂时烧录好特定数据)。如果是后者,需要先用make_ext4fs生成镜像:

mkfs.ext4 -O ^metadata_csum -b 4096 -L FDRAM -d ./frdata_contents frdam.img 512M simg2img frdam.img FDRAM.raw.img

注意-O ^metadata_csum关掉了较新的 metadata 校验特性,部分旧内核的 ext4 驱动不认识它,挂载时会报unsupported filesystem features。这类问题刷机后极难排查,因为内核日志往往被 Android 的 log 系统吃掉了一部分,建议在生成阶段就按目标内核版本核对mkfs.ext4的版本与参数支持范围。

3.4 把变更写进编译产物,并验证 SELinux 标签生效

修改完源码后,编译阶段要特别注意:bootloader 分区表和 dts 的变更会影响boot.img和 bootloader 产物,但 fstab 变更会进入vendor.img或system.img——如果你的工程是动态分区,fstab 所在的分区在编译目录树里甚至不是一个独立目录,而是会被打包进vendor。资源包附带了一个构建后自检脚本:

python3 tool/post_build_check.py --out out/target/product/xxx/vendor

脚本读vendor/etc/fstab,检查by-name/FDRAM是否存在,再解析file_contexts确认/frdata有唯一标签。这两项都通过后,我再接着刷机,不然很容易出现“改动全做了但烧进去的镜像少了某个文件”的尴尬局面。

4. 编译刷机与验证:从亮屏到写入,三步确认改动生效

4.1 分阶段编译,别一次编全量包

新增分区相关的代码分散在 bootloader、内核和 vendor 三处,我不建议上来就跑make全量编译,时间太长,出错时也不好定位。资源包的 README 里推荐的顺序是分产物编译:

source build/envsetup.sh lunch <你的产品>-userdebug # 先编 bootloader 产物,确认分区表无误 make bootloader -j$(nproc) # 再编内核和 boot 镜像 make bootimage -j$(nproc) # 最后编 vendor 以刷新 fstab 和 SELinux make vendorimage -j$(nproc)

三个产物独立编译的好处是:bootloader 编译不过,问题大概率在分区表数组的语法或宏定义;bootimage不过,问题在 dts 节点;vendorimage不过,问题在 fstab 行或file_contexts。分开编让错误定位范围缩小 75% 以上。

编译逻辑上,bootloader 产物不依赖内核,因此最先编能最快验证分区表层;bootimage 依赖 dts 编译进内核二进制,所以第二轮核验设备树;vendorimage 里的 fstab 是纯文本配置,理论上不会编不过,但如果 SELinux 的.te文件语法错误,vendorimage的编译会在 sepolicy 生成阶段中断——不要怀疑,这是最常见的“编不过第三阶段”的原因。

4.2 刷机后的第一轮检查,按 log 顺序排查

刷完机、开机到桌面后,别急着跑业务功能,先用 adb 做三连检查:

# 看分区是否存在及大小是否正确 adb shell ls -l /dev/block/by-name/FDRAM adb shell cat /proc/partitions | grep FDRAM # 看挂载状态 adb shell mount | grep frdata adb shell df -h /frdata # 试写一个文件验证权限 adb shell "echo test > /frdata/probe.txt && cat /frdata/probe.txt"

第一行的输出如果找不到by-name/FDRAM,说明 bootloader 分区表或 dts 至少有一层没生效;第二行mount没输出,说明 fstab 的wait没等到节点、或 fsck 失败把挂载动作跳过了;第三行echo test被拒绝,则是 SELinux 的写入权限没有放到位。按这个顺序逐条排查,基本二十分钟内能定位到具体关卡。

这里有一个血泪经验:每次刷完机后第一件事先看adb shell getenforce。如果返回Enforcing,先执行setenforce 0再测试 FDRAM 写入;如果权限问题消失,说明是 SELinux 规则缺失;如果关了 SELinux 照样写不进去,大概率是文件系统只读或挂载标志少了rw——两者的差异帮你筛掉一半的排查方向。

4.3 恢复出厂与 OTA 场景下的补充验证

常规挂载测试只是第一步,FDRAM 这种新增分区最容易在恢复出厂和 OTA 升级时“现原形”。这个场景下,vold会读取分区表来执行“格式化用户数据分区”的流程,如果它不认识 FDRAM,恢复出厂后新分区的内容不会被清掉,但这既是好事也是坏事——你需要验证的是它会怎么处理未知分区:

# 在恢复模式下手动触发格式化,观察输出 adb reboot recovery adb shell "recovery --wipe_data --wipe_cache"

命令执行后,从 recovery 日志里找 FDRAM 相关行。如果没有任何日志,说明vold的分区类型映射表需要补FDRAM的条目;如果它被当作userdata一并格式化了,那也没问题,但你要确认 FDRAM 里预置的数据不被误删。OTA 升级同理:差分升级脚本通常会把system、vendor等分区列进升级范围,FDRAM 如果不在升级列表里,那新旧版本对它的格式约定必须保持兼容,否则 OTA 后可能挂载失败。

由于这个环节最容易在真机上翻车,我把验证顺序固定成:刷机亮屏 → 三连检查 →setenforce 0对比写入问题 → 恢复出厂测试 → OTA 灰度包冒烟。这套顺序变成了资源包里的verify_fdram.sh,一次跑完并输出对照表,不用人肉盯屏。

5. 避坑指南:FDRAM 挂载最常见的五个“翻车现场”

5.1 现象:开机卡在 bootloader,log 提示 partition duplicate

原因:基线工程里已经存在同名分区,或 bootloader 分区数组前一项的size被改过,导致数组里的start与 dtsreg不一致,bootloader 自检阶段拒绝进入内核。

解决:跑一次check_dup_partition.py,如有重复先给旧分区改名;再跑gen_partitions.py重新生成三处描述文件,确保start由脚本计算而不是手填。我在某款设备上遇到卡 preloader,事后发现是前一个分区的size字段被业务同事手动加了 0x80000,导致 FDRAM 与它重叠了 4MB——这类问题靠肉眼看数组几乎发现不了,必须交给脚本比对。

5.2 现象:挂载成功,但/frdata下所有文件消失,df显示0% used

原因:文件系统类型在 fstab 里写的是auto,内核探测到的却是f2fs或ext4,而分区本身从未被真实格式化过。auto不会帮你执行mkfs,只会帮你猜类型。

解决:fstab 里写死ext4,首次开机的check模式会在文件系统头不合法时自动触发一次格式化。注意这种情况下mount路径会从persist变成erofs相关报错,但不要慌,自检日志里会明确提示filesystem not recognized。资源包的init-fdram.sh脚本里加了首次格式化逻辑,这也是防呆措施,避免每一台设备在产线上人工敲mkfs.ext4。

5.3 现象:mount命令手动执行成功,但 init 阶段自动挂载失败

原因:fstab 里wait和check的顺序写反了。wait必须在check之前,否则 fsck 会在设备节点还不存在时直接失败并跳过挂载。个别厂商的 fstab 解析器对wait,check,fileencryption=software连写没有做重排序,老老实实按依赖顺序排列。

解决:把 fstab 相关参数改成wait,check,不要写成check,wait。另外确认设备节点用by-name而不用mmcblk0pN,因为 N 可能在内核版本升级后变化,by-name是稳定的符号链接。这个坑我碰到过一次:里面明明写了wait,但排在了noatime后面,解析器先处理了noatime再处理wait,行为就变了。

5.4 现象:App 读写/frdata时反复Permission denied,但 shell 下 root 用户一切正常

原因:SELinux 没有为相关 app 域放行。fdr_data_file类型虽然声明了,但untrusted_app域对它的dir/search和file/read权限缺失,root shell 不受 SELinux 限制,当然什么都正常。

解决:按应用的实际需求在untrusted_app.te或业务专属 domain 里追加最小权限。生产环境别直接给untrusted_app开放全局写权限,建议让 App 通过某个系统服务去访问 FDRAM——听起来是架构决策,实际也是安全策略的一部分。如果不方便加系统服务,至少用neverallow反向限制write权限给到特定子集,不要把整个域都放开。

5.5 现象:恢复出厂后,FDRAM 分区仍在,但整个/frdata被 vold 格式化成了 f2fs

原因:vold在 wipe 数据时按分区类型执行格式化,它把 FDRAM 识别成了userdata一类,并套用了默认文件系统策略。这是分区标志位的锅,不是 fstab 能解决的。

解决:回到 bootloader 分区表的.flags,确认它只含PT_USER而不含继承自PT_DATA的默认格式化类型;如果平台要求在fstab的fs_mgr_flags里显式check或者formattable,按厂商提供的分区类型表核对。这个问题没有统一修法,因为不同 BSP 对flags的语义定义不同,但排查思路一致:从 vold 识别到的分区 MTD 名称出发,反查 bootloader 分区表。

6. 进阶技巧:把 FDRAM 用成“日志与缓存自愈区”

FDRAM 分区的天然优势是独立于/data,这意味着系统 OTA、恢复出厂、甚至/data被恶意填满时,它都能保持可用。我的习惯是把它定位成“可重建的临时资源区”——存放系统日志缓冲、OTA 增量包的解压临时目录、以及大尺寸的媒体缓存。

具体的进阶方案是定制一个开机后自检脚本,放在init.frames.rc的on post-fs-data阶段:

service selfcheck_fdram /system/bin/sh /system/bin/fdram_health.sh class late_start user root disabled oneshot on property:vold.decrypt=trigger_restart_framework start selfcheck_fdram

脚本的核心逻辑是:检查/frdata是否可写;若不可写,则尝试umount后重新mkfs.ext4,再挂载回/frdata。这样一来,绝大多数文件系统损坏场景都被自动化兜住——代价是分区内容会丢,但因为定位是“可重建的临时资源”,这个代价是设计上能接受的。这和把重要数据放同一个分区是两种截然不同的设计哲学:FDRAM 要的是“耐操”,而不是“可靠”。

另一个技巧是给它设置配额逻辑:FDRAM 毕竟不是无限大,缓存类写入必须在脚本层做水位线控制。我通常会在fdram_health.sh里加一个df读取,使用超过 90% 时按目录 mtime 顺序删除最老的缓存文件,而不是等极低剩余空间导致 pstore 或 logcat 崩溃。这套“自愈+配额”组合明显减少了售后设备上报“系统卡死但 /data 还有 5GB”之类的相关问题。

从那以后,我每接一个定制项目,只要涉及新分区,都会强制走一遍分区表 → dts → fstab → SELinux → 恢复出厂/OTA 验证的完整流程,再补上自愈脚本,而不是改完 fstab 就交付。这个顺序帮我绕开了绝大多数生产环境才暴露的深坑,希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询