简介:针对MTK平台Android 9.0及以上的分区定制需求,压缩包内提供了在MT6761设备上添加FDRAM新分区并挂载至根目录的完整工程修改。FDRAM(Fast Data RAM)常用于存放快速访问的缓存与临时数据,新增该分区能扩展系统存储能力,这一改动涉及分区表、文件系统与系统配置等多层协同。整个资源包共21个文件,以mk编译脚本、te安全策略、csv配置和py辅助脚本为主,整体仅133KB,轻量却覆盖了分区表调整、文件系统格式化、SELinux权限配置以及挂载所需的关键改动。目前已有682人学习下载,适合正在从事Android系统定制、BSP移植或分区方案设计的工程师对照参考。内容包含device/mediatek配置调整、SELinux权限补充、构建脚本修改及差异对比说明,同时兼顾Android 9.0的A/B更新与Vold框架适配,可帮助深入理解MTK平台分区管理的变化规律,并将方案快速迁移至自身项目中,显著降低试错成本。
1. MTK Android 添加分区并挂载:最坑的不是加分区,而是四处配置不同步
接触过 MTK Android 平台定制的人,多半都碰过“加一个分区”的需求:存储扩容、日志持久化、特殊数据隔离,总之就是在系统里多划一块独立空间。你以为只是改个分区表,实际上 MTK 平台的启动链路是 preloader、lk、内核、init 一棒接一棒,每一棒都可能因为“找不到分区”直接黑屏或反复重启。我之前在一台 MTK 调试机上加 FDRAM 分区就栽过跟头:分区表里加了名字,重启后死活找不到节点,最后还是把 partition table、scatter、内核 DTS、fstab 四个地方全部对齐才跑通。这篇文章按我实际操作的顺序来写,从工具准备到验证跑通,中间穿插具体参数取舍和踩坑记录,适合正在做 MTK Android 设备定制、需要把新分区挂到根目录下使用的开发者。
2. MTK 分区模型与准备工作:先厘清启动链路再动手
2.1 MTK 平台的存储分区模型:LUN、GPT、scatter 的关系
MTK 方案的存储分区不像普通 Linux 机器那么简单直白。eMMC/UFS 的物理存储会划分成若干个 LUN,LUN 0 通常是 user area,所有对用户可见的分区都排在 LUN 0 的 GPT(GUID Partition Table)里。系统启动时,硬件初始化先读 preloader,preloader 再拉起 lk(Little Kernel),lk 准备好显示和基本外设后跳转内核,内核起来之后才轮到 Android 的 init 进程挂载各个分区。这一串环节里,每一级都会读取或校验分区信息:preloader 读 scatter 文件指定的地址来找 boot 镜像,lk 读分区表来传内核参数,内核读 DTS 里描述的存储节点,init 则靠 fstab 决定挂载哪些分区。
所以加一个 FDRAM 分区,本质上是在这条链路上新增一个“所有环节都认识”的存储区域。只改一处、其他环节不认识,就会出现在某个阶段直接停摆的情况。我见过最常见的翻车就是:分区表里加了 FDRAM,但 scatter 文件没有对应条目,导致 preloader 阶段就把整个启动流程打回原点,设备表现为反复重启、进不了系统。
| 启动阶段 | 读取对象 | 修改 FDRAM 分区时涉及的配置 |
|---|---|---|
| preloader | scatter 文件 | 需要加新分区的地址和大小条目 |
| lk | GPT 分区表 | 需要在新分区表中包含 FDRAM |
| 内核 | DTS/DTB 存储节点 | 部分平台需同步存储分区描述 |
| init | fstab / ramdisk | 决定挂载点、挂载时机和权限 |
从这张表就可以看出来,加分区这件事,散的环节多,但每个环节其实都有对应的具体文件。下面先把工具链准备好,再来逐层改。
2.2 工具链清单与版本差异:不同 Android 版本改动位置不一样
做这个操作,需要准备的工具有这么几类:分区表编辑工具(一般是厂商提供的 GUI 工具或可编辑的文本配置)、scatter 文件编辑器(其实就是一个文本编辑器,但要注意保存格式)、boot 镜像打包工具(mkimage 或 mkbootimg 都行)、fastboot 和 adb 命令行工具。其中 fastboot 和 adb 建议用较新版本,Android 10 之后的设备对 fastboot 协议有一定要求,老版本工具偶尔会出现在擦写新分区时无法正确识别分区名的情况。
版本差异是绕不开的。Android 8/9 时代,system、vendor 都是独立物理分区,fstab 放在 ramdisk 里,init.rc 里用mount_all --early做提前挂载,改动起来比较直观。Android 10 开始引入了动态分区(super 分区),system/vendor 被打包进 super,但用户自定义追加的物理分区不受 super 管理,仍然可以直接用 GPT 分区表创建。不过 fstab 的挂载机制变了,early mount 改由 init 第一阶段的 first_stage_mount 处理,分区的文件系统类型若带有加密或 f2fs 这类依赖 vold 的特性,挂载时机更要小心。手头这套方案如果用的是 Android 11 及以上版本,fstab 里的挂载标志位就需要用first_stage_mount这类新语法,而不是老的mount_all --early。这个差异表面上看起来不大,实际改起来逻辑完全不同,后面章节会分别给出写法。
2.3 动手前先把现状固化:记录原分区布局再改
我每次改分区表之前,都会先把设备当前的分区布局完整记录下来,这一步看着多余,但真遇到改坏的场景,它就是后悔药。具体操作分两步:第一步,通过 adb shell 查看当前 by-name 节点和分区大小:
adb shell ls -l /dev/block/by-name/ adb shell cat /proc/partitions第一步命令里,/dev/block/by-name/目录会列出所有分区的符号链接,每个链接指向真实的块设备节点,比如mmcblk0p19。/proc/partitions则提供块设备的主次设备号和大小,单位是 1024 字节块。把这两份输出保存下来,改完后对比用,能第一时间发现哪个分区的起始位置或大小被意外改动了。
第二步,备份当前 scatter 文件和分区表配置。scatter 文件是 preloader 烧录时的寻址依据,不同平台的文件名不同,但内容都是分区的起始地址和大小描述。把原始文件复制一份备份,后续所有改动都基于这份备份来做,防止把平台原有的 bootloader 区域或 BMT(坏块管理表)区域改坏。
提示:MTK 平台的 BMT 和 bootloader 区域位于存储起始位置,这部分地址千万不能手动移动或覆盖,否则设备直接变砖,只能拿盒式烧录器救。
记录完成之后,还要确认一个关键决策:FDRAM 分区放在 GPT 的哪个位置。放在 userdata 后面最安全,因为 userdata 是最后一个用户分区,它后面通常有未分配空间,或者你需要压缩 userdata 的大小来腾出空间。压缩 userdata 的操作影响面更大,因为 OTA 升级和加密分区逻辑都会感知 userdata 大小变化,建议保守处理,优先从存储末尾的未分配区域划空间。
3. 新增 FDRAM 分区:从分区表到 scatter 文件的完整落地
3.1 修改分区表:给 FDRAM 划定大小与位置,注意单位换算
拿到厂商分区表工具后,添加新分区的操作界面通常不会太复杂,核心填写项就是分区名、起始地址、分区大小。但恰恰是这三个字段,单位不一致的问题能把人绕晕:有些工具的输入单位是 KB,有些是 sector(一个 sector 通常是 512 字节),还有的版本直接用十六进制字节数。我一般会在动手前先确认当前 scatter 文件里已有分区的linear_start_addr和partition_size字段,从已有行推算单位。比如相邻两个分区的linear_start_addr之差是0x80000,同时注释里标明该分区大小是 256MB,那么基本可以确定这个差值代表 256MB,而不是 sector 数。
以下是一段新分区在 scatter 文件里的典型新增条目,具体字段名以你手上的模板为准,但逻辑是通用的:
- partition_name: FDRAM type: EXT4 linear_start_addr: 0x1E800000 partition_size: 0x80000000 is_upgradable: true新增条目后,要干一件非常重要的事:检查这条linear_start_addr是否正好等于上一个分区(通常是 userdata 或某个预留分区)的linear_start_addr + partition_size。如果不等于,说明中间有未分配的空间,或者你的起始地址算错了。起始地址算错的后果不是分区变小,而是把别的分区覆盖掉,轻则 userdata 损坏,重则 BMT 区域被破坏直接无法开机。所以这里我每次都会拿计算器手动过一遍十进制换算,确认相等再保存。
大小换算的公式也值得一提。如果模板里partition_size的单位是 sector,512MB 分区实际应填写0x40000(512 * 1024 * 1024 / 512 = 1048576 sector = 0x100000,核对时要注意十六进制别敲错)。这个多一位少一位的错,最容易让分区表工具直接报错或产生重叠分区,宁可多花两分钟验证,也不要凭感觉填。
3.2 scatter 文件同步:与分区表保持一致的七个字段
分区表工具保存后,会生成一份新的分区表镜像,但 scatter 文件本身不会自动更新——需要手动编辑。很多人在这一步漏掉,直接导致后续烧录时 preloader 不认识 FDRAM 分区。scatter 文件中需要同步修改的核心字段包括:partition_name、linear_start_addr、partition_size、type、is_upgradable等。其中linear_start_addr和partition_size必须和分区表工具里填写的内容完全一致,一字不差。
我习惯在编辑完 scatter 后,顺手执行一个 grep 校验,把新增分区的前后几行都打出来看一遍:
grep -n -A 4 "FDRAM" MTK_Scatter_File.txt-A 4表示打印匹配行及其后四行,这样能看到这个分区条目的完整上下文。检查重点有两点:第一,FDRAM 的起始地址是否紧跟在上一分区末尾,中间没有奇怪的偏移;第二,is_upgradable字段是否按预想设置。is_upgradable设为true表示这个分区允许后续通过 OTA 或升级工具更新,如果你只希望 FDRAM 是纯数据分区、不希望被升级流程碰,这里设为false更稳妥。但要注意,设为false后,部分升级流程可能因为无法校验分区版本而跳过它,这在某些方案里会导致升级包里的分区文件版本检查报错,取舍上要看具体的升级策略。
同步完成后,用厂商烧录工具加载修改后的 scatter,确认工具能正确识别 FDRAM 条目,此时烧录工具会显示分区列表,里面有 FDRAM 的名字和大小就说明分区表层面已打通。
3.3 烧录与第一次格式化:让分区真正可用
分区表改完、scatter 同步完后,下一步是把新的 boot 镜像和分区表烧进去。这个阶段我用 fastboot 操作,命令序列如下:
fastboot flash boot boot.img fastboot flash fdram fdram.img fastboot reboot如果手头还没有 fdram.img,可以先用 fastboot 擦除分区,后续再通过 adb shell 格式化文件系统:
fastboot erase fdram这里说一个取舍:第一次烧录时,FDRAM 分区没有文件系统,直接挂载会失败。我一般提前准备好一个 ext4 镜像,用 Android 源码里的工具或者 Linux 主机上的 mke2fs 都能做,命令大致是:
mke2fs -t ext4 -L fdram -b 4096 fdram.img 262144-b 4096指定块大小 4KB,262144是块数量,这个尺寸对应 1GB 分区。做成镜像后再用 fastboot 格式化,比第一次开机后再 adb shell mkfs 要干净,因为能提前确认文件系统创建没有报错。格式化完成后,重新进入系统,ls -l /dev/block/by-name/fdram能看到节点,但这一步只是分区存在,距离挂载到根目录还差配置。
4. 把 FDRAM 挂载到根目录下:fstab、ramdisk 与 SELinux 的配合
4.1 澄清一个关键概念:挂载到根目录与挂在根目录下不是一回事
标题里写“挂载到根目录”,实际操作中绝大多数需求其实是“挂在根目录下的某个子目录”,比如/fdram。把新分区直接挂成根文件系统,意味着 rootfs 整体替换,牵扯到 ramdisk 的启动参数、early console、驱动加载顺序,一旦失败系统起不来,调试难度大得多。我见过有人误以为需求是把 FDRAM 挂成/,结果把根文件系统配置改得面目全非,最后只能重刷。如果你的需求是“新分区在根目录下有一个独立目录可以存数据”,那就走/fdram这个挂载点,这一章讲的都是这个方向。
挂载点的选择也有讲究。不要直接挂在/mnt或/data下,/mnt是标准临时挂载目录,系统服务可能定期清理;/data下有加密和 vold 管理,混进去容易被加密策略误伤。在根目录下新建一个独立目录,比如/fdram,语义清晰,也被 SELinux 和 vold 视为独立存储空间,互不干扰。这个挂在根下的思路,才是标题场景真正要落地的东西。
4.2 fstab 配置:挂载点、文件系统类型与挂载时机的取舍
Android 的 fstab 文件位置随版本变化,Android 8/9 通常在 ramdisk 和/vendor/etc下各有一份,Android 10+ 主要放在 ramdisk 的 first_stage_mount 配置里。不管是哪个版本,FDRAM 的挂载条目逻辑是类似的。以 Android 11 平台为例,fstab 里新增的内容长这样:
/dev/block/by-name/fdram /fdram ext4 defaults wait,first_stage_mount这行配置里,/dev/block/by-name/fdram是通过 by-name 符号链接引用分区,/fdram是挂载点,ext4是文件系统类型,wait表示挂载失败时系统等待重试,first_stage_mount表明这个分区需要在 init 第一阶段就完成挂载。如果不需要这么早挂载,想等 vold 完全起来后再挂,可以不加first_stage_mount,而改用latemount或其他标志,但作为根目录下的固定数据目录,我倾向于 early mount,这样后续服务启动时目录已经可见,避免启动时序上的竞态。
这里有个很关键的细节:为何用/dev/block/by-name/fdram而不是/dev/block/mmcblk0pX?因为mmcblk0pX的分区编号会随着分区表调整而变,同一个物理分区在不同版本镜像里可能对应不同编号,但 by-name 的符号链接名字是稳定不变的。用 by-name 引用,升级或重烧后不必改 fstab,这是运维上最省心的一种写法。
文件系统类型上,ext4 和 f2fs 都常见。MTK 平台不少产品默认 userdata 是 f2fs,但自定义数据分区优先 ext4,原因有二:一是内核和 init 对 ext4 的支持最完善,老版本内核遇到 f2fs 可能存在驱动缺失或挂载参数不兼容;二是后续要 fsck 修复时 ext4 的工具链更成熟。如果你的平台内核版本足够新且明确支持 f2fs,再考虑换,否则用 ext4 是最不折腾的选择。
4.3 ramdisk 与 init 阶段挂载:init.storage.rc 的修改与块设备路径决策
对于 Android 9 及更早版本,光有 fstab 还不够,还要在 init 脚本里显式调用 mount_all。通常在 ramdisk 的init.storage.rc里加入类似下面的一段:
on early-fs mount_all /vendor/etc/fstab.xxx --early--early参数会读取 fstab 里标注了early标志的条目并执行挂载。如果你的 fstab 里 FDRAM 行没有first_stage_mount或early表现,这里就不会挂载。Android 10 及以后版本,系统默认会把 fstab 合成进 ramdisk 的 first_stage_mount 流程,不需要再手写mount_all节点,但如果平台定制过 init 进程,还是要确认 ramdisk 里是否有自定义的init.rc覆盖了默认行为。
修改 ramdisk 后的镜像重新打包也是有讲究的:改完init.storage.rc,要解包 boot.img、替换 ramdisk、再重新打包。打包工具用 mkbootimg 时注意保持原有 cmdline 和 base 地址不变,否则内核启动参数错乱,直接 boot 不起来。命令行大致是:
mkbootimg --kernel kernel.img --ramdisk ramdisk.img \ --cmdline "androidboot.hardware=xxxx" \ --base 0x10000000 --pagesize 2048 -o boot_new.img--cmdline、--base、--pagesize这些参数必须和原 boot.img 完全一致,最保险的方法是先用mkbootimg --dump把原镜像的头部信息导出来,再照着填。这一步属于“改错就黑屏”的高危操作,我往往会多花几分钟核对原镜像参数,而不是凭记忆敲。
4.4 SELinux 上下文与权限:挂载成功但读不到数据的三个检查点
挂载成功不等于能用。Android 的 SELinux enforcing 模式下,新挂载的目录默认 context 可能是u:object_r:unlabeled:s0,这意味着多数进程都无法访问。此时 adb shell 下 root 用户能读写,但 app 层一读写就报 Permission denied,这是典型的“黑匣子问题”——从ls看文件都在,打开就报错。
正确做法是在file_contexts文件里给挂载点定义 context:
/fdram(/.*)? u:object_r:fdram_data_file:s0加入之后需要重新编译 userdata 镜像或重新打包包含 file_contexts 的 ramdisk,让系统加载新的文件上下文定义。临时验证时可以用adb shell setenforce 0把 SELinux 切到 permissive 模式,确认业务功能正常,但正式上线必须把这条策略固化进 file_contexts,否则一次重启就原形毕露。
除了 SELinux,还有两个检查点容易被忽略:一是挂载目录的属主和权限,chown system:system /fdram或按业务需要设为专门的 uid/gid;二是 vold 是否把这个分区当作可移动存储接管。FDRAM 如果出现在 vold 的管理列表里,系统可能把它当成 SD 卡来弹窗提示卸载。若不想让 vold 碰它,需要在 fstab 行里显式标注不要 vold 接管,例如在 flags 里加上voldmanaged=no,或者确保分区类型不在 vold 的扫描范围内。这一点不同平台差异较大,我会先用adb shell vold --help或查 logcat 确认 FDRAM 是否被 vold 注册过,再决定要不要加标志位。
5. 常见排查与避坑:FDRAM 挂载失败的六类现象与根源
5.1 开机反复重启,日志里找不到 FDRAM 节点
现象:烧录完成后设备不停重启,无法进入系统,串口日志或 dmesg 里完全没有 FDRAM 相关输出。
原因:八成是 scatter 文件与分区表地址不一致。preloader 按照 scatter 的地址去存储里找 boot 分区,FDRAM 的条目如果地址写错,哪怕只是偏移了一个 sector,也可能让 preloader 计算出错误的 boot 位置,导致启动链路断裂。另一个常见原因是 LUN 选错:平台默认从 LUN 0 启动,如果你把 FDRAM 或 boot 相关条目写到 LUN 1 的地址空间,启动阶段就找不到。
解决:把 scatter 和分区表工具两边重新对照一遍,确认linear_start_addr、partition_size完全一致。若是 LUN 问题,把新分区条目挪回 LUN 0 的未分配空间。此问题排查时建议把 fastboot 擦除的 FDRAM 分区也重新格式化干净,防止残留的脏文件系统干扰后续挂载。案例:之前某台设备上,我就是把 FDRAM 启始地址算差了一位十六进制数,启动直接卡死在 preloader 的 logo,排查了三小时才从地址上看出问题。
5.2 分区重叠导致 userdata 损坏
现象:烧录新分区后,系统能起来,但 userdata 里的数据全部丢失,或者频繁出现文件系统异常、开机进入恢复模式。
原因:新增分区的起始地址没有放在 userdata 分区结束后,而是直接覆盖了 userdata 的一部分。这类问题往往不是人为乱填,而是单位换算出的低级错误:分区表工具里填的起始地址单位是 sector,但你按 KB 来算,地址就偏小,直接落进了 userdata 的地址范围内。还有一种情况是分区表工具和 scatter 文件用的地址基准不同,一个从存储起始 0 开始计算,一个从用户区域起始开始计算,两者之间的偏移量没补齐,导致所有分区整体错位。
解决:回到 3.1 节的检查方法,用linear_start_addr + partition_size推算上一分区结束地址,确保新分区起始地址紧接其后,且中间不重叠。一旦发现 userdata 已经损坏,先不要继续刷机,把旧的 userdata 镜像备份好,重新往分区表里写入正确地址后,再用 fastboot erase userdata 清掉损坏的数据再恢复。
5.3 挂载成功但 App 层无权限读写
现象:adb shell ls -l /fdram能看到目录,root 下读写正常,但业务 App 往/fdram写文件时直接报 Permission denied。
原因:SELinux context 未配置。新挂载的分区默认 context 为unlabeled,很多进程的策略里禁止访问 unlabeled 文件。另一个可能原因是挂载点目录的 owner 是 root,而 App 使用的 uid 没有写权限。
解决:先在file_contexts里加上/fdram(/.*)?对应的 context,重新打包并刷入;然后确认目录权限,按业务需要设为可写。临时验证用setenforce 0确认 SELinux 是唯一障碍后,再正式改策略。这个坑非常隐蔽,因为现象和路径权限问题几乎一样,不看 audit log 很难定位到 SELinux 层面。
5.4 recovery 模式下看不到 FDRAM 分区
现象:系统正常,但进 recovery 后想挂载 FDRAM 做备份或刷机操作,发现 recovery 里根本没有这个分区。
原因:recovery 使用独立的 ramdisk 和自己的 fstab 配置,和正常系统的 fstab 完全隔离。你改了正常系统的 fstab,但 recovery 的 fstab 没同步,自然看不到新分区。
解决:在 recovery 对应的 ramdisk 里的 fstab 文件同样加上 FDRAM 的挂载条目。这里提醒一点:recovery 的 ramdisk 通常是单独打包在 recovery.img 里的,修改后要重新生成 recovery.img 并刷入。如果平台支持 A/B 分区,还要注意 recovery 对应的是哪个 slot,避免刷错。这个操作不复杂,但容易因为“只改了系统没改 recovery”而在后续维护时卡壳。
5.5 OTA 或升级包刷完后 FDRAM 配置被覆盖
现象:设备做了一次 OTA 或完整升级包刷写后,FDRAM 分区还在,但不再挂载,系统回到没有 FDRAM 的状态。
原因:升级脚本会把系统分区、vendor、fstab 等还原到升级包里的默认版本,你手动改的 fstab 和 file_contexts 都被新镜像覆盖了。如果分区表也被升级流程重建,甚至 FDRAM 分区本身都可能消失。
解决:这不是 bug,是机制决定的。正确做法是把你对 fstab、file_contexts、scatter 的修改提交到设备的 vendor 或 device 配置版本库中,让每一次产线镜像编译都包含这些改动,而不是只改单台调试机。如果你做的是设备量产定制,分区结构的变更是要跟随版本管理的,否则每一台新出厂设备都要手动改,成本完全不可控。另外,部分平台有 vendor_boot 分区,fstab 放在 vendor_boot 里,OTA 升级时如果 vendor_boot 也更新,同样会覆盖配置,这个要确认平台的升级策略。
6. 验证跑通:开机后如何确认 FDRAM 挂载与持久化都正常
6.1 五步走完 FDRAM 挂载验证
设备第一次开机后,按下面这套顺序过一遍,基本能把配置问题都暴露出来。第一步先确认内核层面看到了分区:
adb shell ls -l /dev/block/by-name/fdram看到符号链接指向真实的 mmcblk 设备节点,说明分区表、scatter、内核存储节点这三层已经通了。如果这里都找不到节点,直接回头看第 5.1 节的排查点,不用往下走。
第二步确认挂载状态:
adb shell mount | grep fdram adb shell df -h /fdrammount命令里出现/dev/block/by-name/fdram on /fdram type ext4就说明 fstab 生效了。df -h能看到分区总大小和已用空间,顺便核一下容量是否和你规划时一致,防止地址算错导致分区比预期小。
第三步做一次真实写入,验证目录可写:
adb shell dd if=/dev/zero of=/fdram/test.bin bs=1M count=256 adb shell syncsync确保数据真正落盘,不要直接 shutdown。写入 256MB 文件后,ls -l /fdram/test.bin确认文件大小正确。这一步能同时暴露 SELinux 和权限问题,如果dd就报错,回到 5.3 节检查。
第四步重启设备,验证持久化。重启后再次执行ls -l /fdram/test.bin,文件还在说明挂载配置和文件系统都经受住了重启考验。这个步骤千万别省,很多配置在冷启动后才会暴露问题,比如 fstab 挂载时机不对导致 init 阶段没挂上。
第五步,如果条件允许,把分区写满一次再读回来,确认分区的真实容量和稳定性:
adb shell dd if=/dev/zero of=/fdram/fill.bin bs=1M count=4096 adb shell ls -l /fdram/fill.bin adb shell rm /fdram/fill.bin写满后会触发该分区上的文件系统压力,ext4 如果有隐藏的坏块或地址计算错误,在这一步最容易现形。写满测试后记得把测试文件删掉,保留干净状态。如果写满过程中报 No space left on device,对比df输出和分区规划大小,确认是否地址规划小了一圈。
6.2 强制走一遍五件套检查的习惯
从那次把 userdata 刷坏、又花三小时重烧镜像之后,我给自己立了个规矩:凡是在 MTK 平台上加新分区,强制按“分区表、scatter、内核 DTS、fstab、file_contexts”这五件套逐一核对,一项不过不下刷机线。这个习惯帮我挡掉过至少三次启动黑屏和一次数据分区覆盖事故。你手头如果卡在某一步,把这五件套按顺序过一遍,多半能找到线索。希望帮到你。
本文还有配套的精品资源,点击获取