ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Android系统Ext4文件系统故障排查完整实战指南

Android系统Ext4文件系统故障排查完整实战指南 做Android底层系统维护这么多年我手机里出现频率最高的三类崩溃不是应用崩溃而是开机卡在动画、应用写入数据中途报I/O error、复制文件到一半提示文件系统只读。这三类问题绕不开Android的Ext4文件系统。今天想聊的就是一套从现象到根因的完整排查方法适用所有Android系统工程师、ROM开发者甚至普通用户刷机后遇到类似问题也能照着做。先说结论式的经验遇到Ext4问题最忌讳上来就跑fsck第一步永远是保住日志、确认挂载状态否则现场一丢后面全是瞎猜。1. 先看全局Ext4在Android里负责什么坏了会怎么表现1.1 为什么Android沿用Ext4而不是更“先进”的文件系统Android早期在system分区上用过yaffs2后来大规模切换到ext4原因不是ext4性能天花板最高而是它的工具链和恢复手段极其成熟。e2fsprogs几乎是所有Linux发行版的标配出问题后能通过e2fsck、debugfs、tune2fs做精细化修复这一点对需要长期稳定运行的移动设备太关键了。F2FS虽然针对NAND闪存有更好的写放大控制部分设备的data分区也用了F2FS但从排查者的角度看必须先确认目标分区到底是什么文件系统再决定用哪套工具。这也是很多排查翻车的起点明明设备data分区是F2FS却拿着ext4的dumpe2fs去读读到一堆乱码还说“文件系统损坏”。所以拿到一台设备第一件事永远是看挂载信息。cat /proc/mounts里会明确写ext4或f2fs别凭印象猜。Ext4在Android里通常承担system、vendor、product、cache、data等多个分区根文件系统一般是通过/dev/root挂载的只读ramdisk或system分区这部分挂载参数也会在/proc/mounts里体现。说到根文件系统还有一个绕不开的点就是VFS和sync。VFS是内核虚拟文件系统层用户空间所有open、read、write都会经过这一层sync的作用是把VFS层积累的page cache强制刷到具体块设备上。很多“文件系统卡死”的现象本质不是磁盘物理损坏而是VFS层的大量脏页回写堵住了进程全部卡在D状态表面看起来就是“系统卡在进度条不动”。1.2 故障表现地图不同现象对应不同的排查入口同样是Ext4问题表现差异非常大。我把这些年遇到的现象整理成了一张速查地图方便先对号入座再下手。现象典型分区可能根因开机停在Logo或循环重启system、datafsck失败、superblock损坏、脏journal应用写入数据时报I/O errordata坏块、journal异常、底层块设备错误复制文件到一半提示只读data、sdcard内核检测到错误后自动remount-ro空间“神秘消失”data、cacheinode耗尽、deleted句柄未释放、预留块占比过大应用安装失败或无法建目录dataSELinux标签错误、UID/GID异常、目录项损坏图片/下载文件预览打不开/storage/emulated/0FUSE层权限错乱、文件提供器路径问题需要注意这些只是排查入口不是结论。比如“图片打不开”可能是文件本身没写完也可能是SELinux拦截了读取甚至可能是FUSE转发层的问题跟底层ext4根本没有关系。所以后面的排查流程里我一直坚持先用日志区分层级。2. 排查前必须做的准备现场采集与工具链2.1 第一时间保留现场这些命令一个都不要省文件系统是状态系统一旦重启、重新挂载、跑fsck原本的“案发现场”就可能被改写。拿到问题设备后第一动作是尽量以只读方式把现场数据导出来。下面这套命令是我每次排查都会跑的adb shell su -c dmesg /data/local/tmp/dmesg.txt adb shell su -c logcat -d -v threadtime /data/local/tmp/logcat.txt adb shell su -c cat /proc/mounts adb shell su -c cat /proc/partitions adb shell su -c df -hT adb shell su -c df -i adb shell su -c blockdev --getsize64 /dev/block/bootdevice/by-name/userdata如果设备处于异常状态adb连不上那就进recovery模式抓/sys/fs/pstore这里面通常保留着上次内核panic或OOPS前后的console日志是恢复模式的救命稻草。还有一种情况是data分区加密你会发现dmesg和mount信息都读不出来那就只能连充电状态下进fastboot用bootloader级别的日志辅助判断。这些命令看似简单但经常有人漏掉df -i。df -hT只能看到块空间看不到inode的消耗情况。inode耗尽时df -hT显示剩余几个GB应用却提示存储空间不足不了解这层就会走弯路。2.2 看懂内核日志EXT4-fs error的常见关键词拿到dmesg后不要着急跑fsck先把日志里的ext4关键字、I/O错误串起来看。这里列几个高频关键词和它们背后的含义EXT4-fs error (device dm-0): ext4_find_entry: ... inode #...在访问目录项时发现inode异常通常是文件系统元数据不一致。EXT4-fs error (device dm-0): ext4_lookup: deleted inode referenced目录项还在但对应inode已经被标记删除典型的目录项与inode表不同步。EXT4-fs error (device dm-0): ext4_journal_start_sb: Detected aborted journal日志已经中止说明上一次运行时发生过错误系统选择停用日志避免进一步破坏。I/O error, dev mmcblk0, sector ...块设备层报错问题可能出在eMMC/UFS控制器、坏块映射表不一定是文件系统逻辑问题。Buffer I/O error on device dm-0, logical block ...VFS层无法完成块读写常见于底层存储掉线或供电不稳。remounting fs read-only内核检测到多次错误后主动把分区挂载成只读这是保护动作避免继续写入扩大损坏范围。我见过一个最典型的情况设备在低电量下频繁关机dmesg里全是Buffer I/O error但ext4自身逻辑并没有损坏。这种问题光修文件系统没用得先换电池、换数据线、排除硬件因素否则修完马上又坏。2.3 工具链选择Android里怎么凑齐e2fsck和debugfs原生Android镜像里通常没有完整的e2fsprogsdebugfs、e2fsck、dumpe2fs这些命令不是缺这就是缺那。三种可行路径一是用TWRP这类第三方recovery它自带e2fsck而且是在分区未挂载的状态下执行修复更安全。二是用BusyBoxBusyBox的e2fsck功能相对完整但版本老遇到新特性如metadata_csum可能不识别。三是自己交叉编译静态版e2fsprogs用NDK工具链编完push到/data/local/tmp再chmod 755执行。我不太建议在Android系统内直接安装那种“一键修复”工具很多实现会先挂载分区再跑fsck这在文件系统处于错误状态时风险极高。要跑写修复必须保证目标分区没有挂载或者处于只读状态。这也是为什么我习惯进recovery再操作安全边际高得多。3. 一次真实排查流程从“应用优化中卡死”到修好/data分区3.1 现象与初判有台测试机的data分区断电重启后一直卡在“正在优化应用”的界面过了30分钟还在转圈。重启第二次情况更糟直接停在开机Logo。这种场景抽屉里八成是data分区ext4出了问题。先不进recovery清数据而是尝试通过adb连进去看状态。结果adb能连上我第一时间抓了dmesg。日志里能看到连续多次EXT4-fs error (device dm-0): ext4_find_entry: reading directory #2 offset 0Aborting journal on device dm-0.EXT4-fs (dm-0): previous I/O error detected for journal dev dm-0这就很清楚了目录项读取失败journal被中止系统大概率会remount-ro。再执行cat /proc/mounts果然/data显示为ro。这时候你就是写什么都写不进去也不用担心误操作扩大损坏但也不能直接拔电因为还有一部分脏页在内存里没刷下去突然断电可能让情况更乱。正确操作是保持设备通电把状态记下来然后想一个稳妥的修复方案。我此时还顺手执行了ls -l /dev/block/platform/*/by-name/确认data分区的实际块设备路径方便后面直接在recovery里操作。3.2 逐层定位读superblock、inode和journal状态分区没挂载时要看文件系统状态我用dumpe2fs只读模式读superblockdumpe2fs -h /dev/block/bootdevice/by-name/userdata重点看几项Filesystem state是clean还是not cleanErrors behavior是Continue还是Remount read-onlyJournal size多大Filesystem features里有没有开启metadata_csum、64bit。如果Filesystem state显示with errors说明上次运行已经记录了错误即使不修复下次挂载也会启用错误处理逻辑。再进一步查具体inode用debugfs进入只读模式debugfs -R stat 8 /dev/block/bootdevice/by-name/userdata8是根目录的inode如果这里都读得出来说明superblock和根inode还没完全坏如果这一步就报错那就得考虑恢复备份superblock。Ext4通常在块组0里有主superblock块组1、2等会有备份恢复方法是e2fsck -b 32768之类但这部分操作已经属于高级修复没有完整备份前不建议新手直接上手。3.3 e2fsck实战修复命令、退出码和操作纪律确认分区没有挂载后我先进recovery模式然后跑检查e2fsck -fn /dev/block/bootdevice/by-name/userdata-f强制检查即使文件系统状态看起来clean也检查一遍-n是只读模式所有问题都回答no不写入任何修改。这一步是侦查输出会告诉你具体哪些inode有重复块、哪些目录项引用了空inode。我的习惯是先把完整输出导出来e2fsck -fn /dev/block/bootdevice/by-name/userdata /tmp/fsck_report.txt如果侦查结果只是少量孤儿inode我会直接做修复e2fsck -fy /dev/block/bootdevice/by-name/userdata-y表示对所有问题自动回答yes适合无人值守的交互式修复。注意千万不要对正常挂载使用的系统分区直接执行写修复必须有把握当前分区未挂载。这一个纪律比我见过许多人踩过的坑都更深。e2fsck退出码也是重要的判断依据退出码含义0无错误1文件系统错误已修复2文件系统已修复需要重启系统4文件系统错误未修复8操作错误16用法或语法错误32检查被用户取消128共享库错误上次修完看到退出码1但重启后又出现相同问题这意味着硬件层面可能有干扰不是软件能完全兜底的。3.4 修复还没完重新挂载之后必须验证syncfsck成功后很多人直接重启然后发现问题复发。原因通常是没检查底层块设备状态也没验证VFS层能否正常回写。我会先挂载分区再执行一次完整写入验证mount -t ext4 /dev/block/bootdevice/by-name/userdata /mnt dd if/dev/zero of/mnt/testfile bs1M count1024 convfsync syncdd结束后的sync很关键它强制把page cache刷到磁盘如果这一步卡住或者报错说明底层存储还有问题别急着收工。我再提一句Android的正常数据写入路径里sync和fsync是有区别的sync是全局回写所有脏页fsync是只针对某个文件的fd做落盘。应用频繁调用fsync虽然保证数据安全但会放大写次数长期跑下来eMMC/UFS寿命消耗更快。排查时如果发现jbd2/dm-0-8内核线程CPU占用居高不下八成就是日志提交太频繁同时伴随大量fsync的调用。4. 容易误判成“Ext4损坏”的路径与权限问题4.1 用户天天遇到的/storage/emulated/0/android/data/...到底是什么热搜词里高频出现/storage/emulated/0/android/data/com.tencent.tmgp.sgame/...这类路径很多是游戏、应用下载、照片缩略图场景。这个路径看起来像是真实文件系统路径实际是FUSE层挂载出来的虚拟目录底层对应/data/media/0/Android/data/...。这意味着你在adb shell里看到的/storage/emulated/0/android/data/...不一定能直接当作真实存储路径去访问尤其是Android 10之后分区存储收紧普通应用已经不允许通过这个绝对路径去读其他应用的数据目录了。遇到operation not permitted、Permission denied这类错误第一反应不要是“ext4坏了”。我一般会这样做先ls -lZ /storage/emulated/0/Android/data/目标包名看SELinux上下文和属组再stat /data/media/0/Android/data/目标包名确认底层inode存在。如果底层inode存在且能访问那问题就在FUSE层的权限转发或Scoped Storage策略跟文件系统无关。如果底层inode都不存在那才可能涉及文件被清理或者目录项损坏。4.2 SELinux、DAC和UID/GID为什么报EACCES而ext4没问题应用创建不了目录、写不了文件经常是SELinux在拦截而不是ext4的逻辑有问题。判断方法很简单看dmesg里有没有avc: denied关键字dmesg | grep avc输出里会有一段格式类似于avc: denied { write } for pid12345 commapp namedata devdm-0 ino456 scontextu:r:untrusted_app:s0 tcontextu:object_r:app_data_file:s0 tclassdir这里的关键是tcontext的类型。app_data_file应该是应用私有目录的标准类型如果看到system_data_file或unlabeled说明SELinux上下文错了。修复用restorecon通常不直接chcon因为restorecon按文件系统的默认标签规则恢复更标准restorecon -R /data/data/包名还要注意ext4挂载时有没有正确传递context参数。某些自定义ROM改乱了fstab.qcomdata分区挂载时没带SELinux context就会导致整个分区目录全部变成unlabeled。这种情况不只是某个应用的问题系统大量功能都会异常。4.3 空间消失、inode耗尽与“删不掉”的文件空间“神秘消失”不是一个假问题在Ext4里至少有三个常见来源。一是mke2fs默认预留5%的块给root用这些空间df统计不出来在容量小的分区上尤其明显。如果分区用到99%可以考虑调整预留比例tune2fs -m 0 /dev/block/bootdevice/by-name/userdata但我不建议长期设成0毕竟预留块在文件系统碎片化严重时还有缓冲作用。二是inode耗尽。分区格式化时就固定了inode总数每个小文件都要占用一个inode尤其应用缓存目录里经常产生数十万个小文件很容易把inode池占满。df -i看到IFree为0时即使df -h还有几个G也创建不了新文件。这个靠删文件能缓解但真正要彻底解决只能重新格式化分区或者修改打包时mke2fs的-i参数。三是deleted但被进程持有的文件。删除文件时只要还有进程保留着fd空间就不会真正释放。排查方式ls -l /proc/*/fd/ 2/dev/null | grep deleted找到对应进程并重启或杀掉它空间才会回来。这个现象在升级OTA、安装大型游戏后特别常见所以遇到“空间越删越小”先查fd再查inode最后才怀疑文件系统损坏。5. 高频故障速查表与避坑心得5.1 典型故障速查表故障现象可疑层快速定位处置建议开机停在Logo反复重启data分区文件系统错误dmesg里有无EXT4-fs error/proc/mounts里data是否为rorecovery下e2fsck -n检查再e2fsck -fy修复修复不了只能格式化新安装应用写不了数据SELinux或DAC权限dmesg grep avcls -lZ /data/data/包名restorecon -R修正标签检查fstab挂载参数下载进度条卡死不动VFS层回写、底层存储I/Odmesg有无Buffer I/O errortop查D状态进程确保供电稳定执行fstrim必要时质检存储硬件删除大文件空间未释放进程fd持有ls -l /proc/*/fd/ grep deleted重启相关进程空间会释放剩余空间很多但提示空间不足inode耗尽df -i看IFree清理海量小文件或重新格式化第三方应用访问不了别的应用data目录Scoped Storage/FUSE路径规则、content provider策略使用框架提供的MediaStore、FileProvider API复制大量小文件极慢ext4元数据操作频繁iostat -x看utildmesg有无journal阻塞适当调大journal避免碎片化考虑换F2FS5.2 踩过最深的几个坑这些年我踩过的坑不少挑几个最典型的说说。第一不要一上来就跑e2fsck -fy。我早期处理过一台设备data分区报告有错误我直接跑-fy结果把能恢复的目录项全标记成“孤儿文件”清理完毕后应用数据反而更完整地丢了。先跑-fn只读侦查看清楚错误类型再决定是否自动修复这个顺序救过我很多次。第二每次断电或异常重启前一定要考虑供电稳定性。看起来是Ext4反复损坏最后发现是测试机电池老化电压一低就掉盘。换完电池什么问题都没了。文件系统的错误日志有时候只是“结果”不是“原因”。第三ROM开发者很容易忽略fstab里的挂载参数。一个典型的错误是把dataordered改成datawriteback以为能提速结果异常断电后数据和元数据不一致journal也救不回来。没有充分验证前不要动这类默认参数。第四大量使用sync并不等于“数据安全”。移动设备上每天会有成千上万次page cache回写sync只是全局触发一次异常断电后真正能保证元数据不损坏的是journal但journal不保证数据块已经落盘。所以在关键业务里该用fsync的时候还是要用同时要注意它的调用频率。第五很多“文件系统问题”最后其实在FUSE层。/storage/emulated/0/android/data/...路径的权限、属组、SELinux上下文和底层ext4完全是两回事。判断时先确认底层inode是否存在再谈上层权限别让用户拿着一个Permission denied的报错去格式化手机。最后说说我的整体体会。遇到这类问题我的口径永远是先存日志、再看mount、再碰fsck。日志是第一现场mount决定了文件系统当前状态fsck是最后手段。这套流程走下来绝大多数Ext4相关故障都能定位到具体层级而不是笼统地归咎于“系统卡了”或“存储坏了”。真到了需要格式化的时候也一定要确认备份已经完整导出否则数据丢了换哪个文件系统都没用。
返回列表