文件系统跨平台适配与嵌入式实战:从VFS原理到RK3588排障
文件系统这四个字在大多数应用开发者的日常里几乎是无感的——你调用一个 open()或者高级语言里一句 File.ReadAllBytes()事情就成了。但只要你的代码需要同时跑在 Windows 开发机、Linux 服务器、macOS 笔记本以及一块 RK3588 这样的嵌入式板子上跨平台适配这五个字就会用最不讲道理的方式撞上来换行符不一样、路径分隔符不一样、大小写规则不一样、权限模型不一样、时间戳精度不一样、文件名里能出现的字符也不一样。而这些差异最后都会收敛到同一个地方——文件系统这一层。我这些年做的东西比较杂从嵌入式板子的根文件系统裁剪到集群上做 HDFS 命令操作和 GPFS 换盘再到帮同事收拾因为共享目录挂载失败而卡了两天的虚拟机踩过的坑基本都绕不开文件系统。这篇文章想把这些散落的经验串成一条线先讲清楚文件系统在 Linux 里到底扮演什么角色、VFS 这层抽象是怎么工作的再落到跨平台适配时真正会咬人的那几个差异点最后用 RK3588 上搭 Ubuntu 根文件系统、NFS 远程挂载、HDFS 命令操作、GPFS 换盘这几个具体场景把原理落成可以照着敲的命令。适合谁看如果你是刚接触嵌入式或者 Linux 运维的同学这里面的分区规划、挂载顺序、排障思路能帮你省掉大量瞎试的时间如果你已经写过跨平台代码那前面关于大小写、权限位、编码的那几节可以当作一份自查清单。原理我会讲透命令我也会给全你完全可以挑自己需要的那一段直接抄。1. 从一次诡异的挂载失败说起为什么报错信息这么难懂1.1 一条让人摸不着头脑的报错文件系统特定的 lookupandopen[file] 实施失败——这条报错我第一次看到的时候盯着看了至少有五分钟。字面意思翻译过来是某个具体文件系统在实现查找并打开文件这个操作时失败了。问题在于它没告诉你哪个文件系统、哪个文件、为什么失败。后来复盘这个场景的根因其实很朴素宿主机是一台 Windows在虚拟机里挂了共享目录宿主机那边有个文件名里带了冒号或者问号这类字符Linux 侧的文件系统层在解析这个名字时就炸了。报错之所以绕是因为它来自文件系统驱动的回调层而不是上层应用所以错误信息只能给到某一类操作失败给不出具体名字。这件事给我的教训是看到文件系统相关的报错第一反应不要去看应用日志先去看挂载点状态和内核日志。mount | grep 挂载点、df -hT、dmesg | tail -50这三条命令能解决掉一大半的困惑。内核日志里通常会有更具体的原因比如设备忙、协议版本不匹配、路径不存在、权限被拒。1.2 文件系统到底管了哪几件事要理解报错先得知道文件系统这份工作说明书里写了什么。一个文件系统在操作系统里至少要负责四件事一是把块设备上一串连续或者离散的扇区组织成有层次的名字空间也就是目录树二是记录每个文件的数据块落在哪些物理位置这就是分配策略ext4 用的是 extentFAT 用的是簇链三是维护元数据包括大小、权限、所有者、时间戳、链接数四是保证一致性也就是崩溃或者掉电之后还能不能把文件系统挂起来。这四件事里第一和第三是最容易在跨平台时出问题的。名字空间涉及路径分隔符、大小写、非法字符元数据涉及权限位、所有者、时间戳精度。而第四件事是所有嵌入式工程师心里的那根刺——因为板子随时可能被拔电。我习惯把文件系统理解成一本书的目录加索引。目录是名字空间索引是 inode 或者 FAT 表而书页就是数据块。书的装订方式可以有很多种但读者翻书的方式是统一的——这个统一的翻书方式就是下一节要讲的 VFS。注意凡是文件系统层面的报错先怀疑输入侧的名字或者路径是否合法再去怀疑存储介质本身。顺序反了会浪费大量时间。2. VFS 抽象层Linux 怎么把几十种文件系统塞进同一套接口2.1 VFS 的四个核心对象Linux 支持 ext4、XFS、Btrfs、F2FS、FAT、NTFS、NFS、CIFS、squashfs、overlayfs 等等几十种文件系统但你在用户态写代码的时候用的永远是同一套系统调用。这中间的转换工作由 VFSVirtual File System虚拟文件系统来完成。VFS 用四个核心数据结构来描述一切对象结构体对应现实中的东西超级块super_block一个已挂载的文件系统实例索引节点inode一个文件或目录的元数据目录项dentry路径中的一个名字片段与 inode 的映射文件对象file一个进程打开某个文件后的句柄超级块是这个文件系统整体什么样索引节点是这个文件本身什么样目录项是这个名字指向哪个文件文件对象是我这次打开它要干什么。四层各管一摊彼此解耦这就是 VFS 能同时容纳这么多具体文件系统的原因。具体文件系统要做的就是填一张函数指针表。ext4 填一套FAT 填一套NFS 填一套。VFS 调用统一的接口实际执行的是各家的实现。前面那条文件系统特定的 XXX 实施失败的报错说的就是这个函数指针表里某个函数返回了错误码。2.2 一次 open() 调用在内核里走过的路理解了对象模型再看一次 open(/data/app.conf, O_RDONLY) 的完整链路就顺了。第一步是路径解析。VFS 拿到 /data/app.conf从当前进程的根目录或者当前工作目录出发逐段查找。先找 /再找 data再找 app.conf。每一段名字都会先去 dentry 缓存里查命中就直接用没命中就调用具体文件系统的 lookup 函数去磁盘上找。这一步是纯元数据操作不读文件内容。第二步是权限检查。拿到 inode 之后VFS 会比对进程的 uid、gid、附加组和 inode 上的 mode 位判断是否允许打开。这一步在跨平台场景里非常关键因为 Windows 的 ACL 模型和 Linux 的 mode 位模型不是一回事共享目录里经常出现文件明明在那儿就是打不开的情况。第三步是创建 file 对象把 dentry、inode、偏移量、打开标志、文件操作函数表都挂上去返回一个文件描述符。之后读数据走 page cache缺页时再调用具体文件系统的 readpage 或者 readahead。整个链路里只有 lookup 和 readpage 这两步是真正落到具体文件系统身上的其余全是通用逻辑。这也解释了为什么文件系统出问题表现往往是能 ls 不能 cat或者能打开不能读——故障点落在了不同的层。2.3 为什么这套设计对跨平台适配这么重要如果没有 VFS每接一种存储后端上层程序都得改一遍代码。有了 VFSLinux 侧的程序可以完全无视底层是本地盘、网络盘还是内存盘。NFS 之所以能当成本地目录一样用就是因为它实现了 VFS 的接口。但这个便利是有代价的VFS 的语义是按 Linux 的习惯设计的天然假设大小写敏感、有权限位、有硬链接。当底层实际是个 FAT 分区或者 Windows 共享目录时这些假设就不成立VFS 只能靠假装来圆场。圆得好的部分你感觉不到圆不好的部分就变成了你排查半天的诡异 bug。3. 根文件系统内核启动之后的第一件大事3.1 initramfs 与 switch_root 的分工内核启动到最后阶段会去找根文件系统。但这里有个鸡生蛋的问题要挂载 eMMC 或者网络上的 NFS可能需要先加载驱动模块、做网络配置而这些模块和配置本身又放在根文件系统里。解决这个矛盾的办法是 initramfs——一个被打包进内核镜像的小型内存文件系统。流程是这样内核先解压 initramfs 到内存把它的根目录设成临时根然后执行里面的 /init。这个脚本负责加载必要模块、挂载真正的根文件系统到 /newroot最后调用 switch_root 切换到新根并把临时根删掉。在嵌入式场景里如果内核自带了所需的存储驱动也可以把 initramfs 省掉直接在启动参数里指定 root 设备。RK3588 这类 SoC 上最常见的做法是用 U-Boot 的 distro boot通过 extlinux.conf 或者 boot.scr 传入 root/dev/mmcblk0p2 rw rootwait 这样的参数。rootwait 这个参数一定要带因为 eMMC 的枚举是异步的不带它内核可能在设备还没就绪时就去挂根结果就是经典的 VFS: Cannot open root device 然后 panic。3.2 一个能跑的根文件系统里得有什么很多人第一次裁根文件系统的时候会误以为只要有 /bin 和 /lib 就够了。实际上最小可启动的根文件系统需要这些目录各司其职目录作用常见踩坑/bin /sbin基本命令busybox 软链指向错误路径会全部失效/lib /usr/lib动态库和内核模块aarch64 与 armhf 混用会导致 exec format error/etc配置、fstab、passwd缺 /etc/passwd 时某些服务直接拒绝启动/dev设备节点静态节点和 devtmpfs 冲突一般交给内核自动挂/proc /sys内核接口不挂载会导致大量工具报错fstab 里要写/var /tmp运行时数据只读根文件系统时这两个必须挂 tmpfs/root /home用户目录权限没设对串口登录会失败/etc/fstab 是很多人忽略的一环。如果 proc、sysfs、tmpfs 没有在 fstab 里声明systemd 或者 init 脚本可能会报一堆无关痛痒但很吵的错误。我一般会在 fstab 里明确写上proc /proc proc defaults 0 0 sysfs /sys sysfs defaults 0 0 devtmpfs /dev devtmpfs defaults 0 0 tmpfs /tmp tmpfs mode1777,size256M 0 0 tmpfs /var/volatile tmpfs mode0755,size512M 0 0/tmp 的 mode 一定是 1777那个 1 是 sticky 位保证用户只能删自己的文件。这个细节在只读根文件系统方案里特别重要漏了之后很多程序会报权限错误。4. 跨平台适配真正会咬人的五个差异点4.1 大小写敏感性Windows 不区分Linux 区分这是最经典的一个。在 Windows 上README.md 和 readme.md 是同一个文件在 Linux 的 ext4 上这是两个完全独立的文件。结果就是代码在 Windows 上跑得好好的一部署到 Linux 就报找不到文件。更麻烦的是 macOS。APFS 默认对大小写不敏感但保留大小写这意味着你git add Readme.md之后别人在 Linux 上 checkout 出来还是 Readme.md但如果你在 macOS 上重命名成 readme.mdGit 可能根本记录不到这次变更。我的做法是所有代码仓库统一约定用小写加连字符命名并在 CI 里加一条检查扫描是否存在仅大小写不同的同名文件。这条检查拦下来的问题比很多单元测试都值钱。4.2 权限位与所有者FAT 根本没有这个概念Linux 的每个文件有 12 位权限信息包括 9 位 rwx 和 setuid、setgid、sticky 三个特殊位。FAT 系列文件系统压根没有权限位这个概念挂载时必须靠 mount 参数统一指定比如umask022、uid1000、gid1000。这就带来一个问题你在 FAT 分区上解压一个 tar 包里面的可执行位全部丢失。等再拷回 ext4脚本就不能执行了必须手动 chmod x。NTFS 稍微好一点Linux 侧的 ntfs-3g 驱动支持通过扩展属性模拟权限但需要挂载时加上permissions选项而且和 Windows 侧的 ACL 并不严格对应。跨平台协作时最稳妥的办法是代码和脚本统一用 git 管理git 会记录可执行位大文件用 tar 打包传输不要直接拷目录。4.3 路径分隔符与非法文件名字符Windows 用反斜杠Linux 用正斜杠。好在绝大多数现代语言和库都做了归一化处理真正的问题往往出在两层一是字符串拼接二是配置文件里硬编码的路径。字符串拼接我建议一律用语言自带的 APIPython 用 os.path.join 或者 pathlibJava 用 Paths.getC 用 std::filesystem::path。手写拼接在 Windows 上遇到 C: 加 / 的组合能给你拼出一堆奇怪的路径。非法字符更隐蔽。Linux 侧文件名里除了斜杠和空字符几乎什么都能放包括冒号、问号、星号、引号、换行。Windows 侧这些全是禁区。macOS 侧还有自己的坑文件名里带冒号会被系统悄悄转成斜杠而文件名里带斜杠又会被转成冒号来回转几次名字就面目全非了。我的经验是跨平台共享的目录里文件名只允许出现字母、数字、下划线、连字符、点这五类字符其他一律改造。这条规则看起来粗暴但它能挡掉 90% 的诡异挂载失败。4.4 换行符与文本编码CRLF 和 LF 的问题老生常谈但它的影响面比很多人想的大。不只是 git diff 显示全文件变更shell 脚本里如果混了 CRLF执行时会报bad interpreter: No such file or directory因为内核把 \r 当成了解释器路径的一部分。这个报错极具误导性很多人会去查解释器路径其实用file script.sh看一眼就真相大白或者sed -i s/\r$// script.sh一键解决。编码问题在中文环境更常见。Windows 上的文本文件默认可能是 GBK 或者带 BOM 的 UTF-8Linux 侧默认 UTF-8 无 BOM。BOM 尤其讨厌它会在文件开头插入三个字节导致某些编译器和解释器把第一个 token 解析成垃圾。我给团队定的规矩是所有文本文件统一 UTF-8 无 BOMLF 换行在 .gitattributes 里写死。* textauto eollf *.bat text eolcrlf *.png binary *.so binary这三行加上几条二进制声明能省掉无数次为什么在别人机器上编译不过的扯皮。4.5 时间戳精度与排序FAT 的时间戳精度是 2 秒而且不记录时区。NTFS 是 100 纳秒ext4 取决于 inode 大小256 字节的 inode 支持纳秒级。当你在不同文件系统之间拷贝文件时时间戳会被截断或者偏移依赖时间戳做增量同步的工具就可能漏文件或者重复传。rsync有个--modify-window参数就是专门解决这个问题的在 FAT 和 ext4 之间同步时一般设成 2 秒。另外要注意Linux 默认可能启用了 relatime也就是只有访问时间早于修改时间或者超过 24 小时才更新 atime这会让基于 atime 的清理脚本行为不符合预期需要的话得改成 strictatime虽然会带来额外的写放大。5. sync 与数据落盘断电之后文件为什么会变 0 字节5.1 writeback 机制到底在等什么Linux 的写操作默认是写回模式。你调用 write() 之后数据只是进了页缓存函数就返回了磁盘上什么都没变。内核有后台线程定期把脏页刷下去这个间隔由几个参数控制参数默认值含义dirty_background_ratio10脏页占内存 10% 时开始后台回写dirty_ratio20脏页占内存 20% 时阻塞写入进程dirty_expire_centisecs3000脏页最长驻留 30 秒dirty_writeback_centisecs500回写线程每 5 秒醒一次这四个参数一组合你就明白了在极端情况下一份刚写完的数据可以在内存里待 30 秒才落盘。这 30 秒里如果掉电数据就没了。掉电后文件变成 0 字节通常是因为 inode 上的 size 字段被更新了这是元数据日志会保护但数据块还没写下去。mount 时文件系统做日志回放元数据恢复了指向的块里却是空内容。ext4 的 dataordered 模式能避免大部分这种情况因为它在写元数据之前会先保证数据落盘。5.2 fsync、fdatasync、sync、syncfs 怎么选这几个函数经常被混用其实分工很明确。sync()是把整个系统所有文件系统的脏页都刷下去副作用大耗时不可控。一般只在关机、重启这种场合用比如sync; sync; reboot这个经典组合。syncfs(fd)只刷 fd 所在的那个文件系统比 sync 精准比 fsync 粗适合我要保证这一批文件都落盘的场景。fsync(fd)刷指定文件的全部数据和元数据最常用也最慢。数据库的 WAL 日志、配置文件的原子写入都得靠它。fdatasync(fd)只刷数据以及必要的元数据比如文件大小和块映射但不保证 mtime、atime 这些无关键落盘。对于日志追加这种只要大小对就行的场景fdatasync 比 fsync 快不少。写配置文件的标准姿势是先写临时文件fsync 它然后 rename 覆盖原文件再 fsync 父目录。最后那一步很多人会漏但 rename 这个操作的持久化依赖父目录的元数据不刷父目录的话崩溃后可能看到旧文件。5.3 嵌入式场景的掉电保护实践在 RK3588 这类设备上掉电是常态。我的做法分几层。第一层日志文件系统加数据模式。ext4 挂载参数用dataordered日志提交间隔commit5这是性能和安全的平衡点。如果存储是 eMMC 且对寿命敏感可以考虑noatime减少写入。第二层关键数据用双备份加校验。比如设备配置写两份一份带 CRC启动时校验失败就回退到另一份。这个方法听起来笨但在实际项目里救过我很多次。第三层最关键的数据用 NOR Flash 或者带掉电保护的 eMMC。有些低成本 eMMC 在写入过程中掉电会直接把某个块写坏。这个风险纯软件解决不了只能靠硬件选型和冗余。注意noatime和nodiratime一起用效果更好前者管普通文件后者管目录。但relatime通常是更好的默认选择兼容性和性能都不差。6. 实战在 RK3588 上搭一套 Ubuntu 20.04.5 根文件系统6.1 分区规划和文件系统选型RK3588 的存储一般是 eMMC容量从 8GB 到 64GB 不等。我的分区方案通常是三区# 使用 gdisk 分区GPT 表 序号 起始扇区 大小 用途 1 32768 16MB uboot 区域idbloader uboot.itb 2 65536 4GB rootfsext4 3 8454144 剩余 dataext4 或 f2fs起始扇区为什么是 32768因为 Rockchip 平台的引导链把 idbloader.img 放在 64 扇区32KB处u-boot.itb 放在 16384 扇区8MB处加起来的 16MB 空间必须留出来不能被分区覆盖。这个和 x86 的 1MB 对齐规则不一样是 Rockchip 特有的。文件系统选型上rootfs 我用 ext4因为工具链支持最好、修复手段最多。data 分区如果写入频繁且容量小可以考虑 F2FS它是为闪存设计的随机写性能更好但修复工具不如 ext4 完善。只读的根文件系统可以用 squashfs 加 overlayfs能显著降低损坏概率。6.2 用 debootstrap 构建根文件系统从零手工拼一个 Ubuntu 根文件系统太费劲用 debootstrap 最省事。# 在 x86 主机上构建 arm64 根文件系统 apt-get install debootstrap qemu-user-static binfmt-support mkdir -p rootfs debootstrap --archarm64 --foreign focal rootfs \ http://ports.ubuntu.com/ubuntu-ports/ # 把 qemu 静态二进制拷进去用于第二阶段 cp /usr/bin/qemu-aarch64-static rootfs/usr/bin/ # 第二阶段在 chroot 环境里完成配置 chroot rootfs /debootstrap/debootstrap --second-stage--foreign的意思是在当前架构上只做第一步第二步放到目标架构里执行。拷贝 qemu-aarch64-static 之后用 binfmt_misc 注册x86 内核就能直接执行 arm64 的二进制了。第二阶段完成后进 chroot 配 apt 源、装内核模块、设密码、配网络。这里有几个必做的动作# chroot 之前先挂载必要目录 mount -t proc /proc rootfs/proc mount -t sysfs /sys rootfs/sys mount -o bind /dev rootfs/dev mount -o bind /dev/pts rootfs/dev/pts不挂这些apt 装包时会报一堆莫名其妙的错误。装完之后记得 umount 干净否则打包镜像时会带上不该有的东西。6.3 用 NFS 远程挂载做开发调试板子上反复烧写 eMMC 是最耗时间的环节。更好的做法是让板子通过 NFS 挂载根文件系统改完主机上的文件板子重启就能生效。主机侧配置# /etc/exports /nfsroot/rootfs *(rw,sync,no_root_squash,no_subtree_check) # 生效 exportfs -ra systemctl restart nfs-kernel-serverno_root_squash是必须的否则板子上的 root 用户写文件会被映射成匿名用户权限全乱。sync保证写入立即落盘调试阶段宁可慢一点也不要出错。板子侧的内核启动参数root/dev/nfs rw nfsroot192.168.1.100:/nfsroot/rootfs,vers4.1,prototcp ip192.168.1.200:192.168.1.100:192.168.1.1:255.255.255.0::eth0:off consolettyS2,1500000n8ip这一串的格式是固定的七段本机 IP、服务器 IP、网关、掩码、主机名、网卡名、协议。少一段或者顺序错了内核就找不到 NFS 服务器。我第一次配的时候就是漏了最后的 off卡了半小时。vers4.1我建议显式指定。NFSv3 和 v4 在锁机制、ACL 支持上差别不小不指定的话内核会协商不同内核版本协商结果可能不一样换台机器就复现不了问题。6.4 首次启动的几个常见坑板子第一次起来黑屏或者 panic八成是下面几个原因之一。第一个是串口参数不对。RK3588 的调试串口通常是 ttyS2波特率 1500000。这个波特率有些串口工具不支持需要手动输入。看着像没输出其实只是没对上。第二个是 init 没找到导致Kernel panic - not syncing: No init found。检查 /sbin/init 是否存在、是否有可执行权限、动态库依赖是否完整。用file和ldd在主机上就能提前检查。第三个是/etc/fstab里写了一个不存在的分区systemd 进入紧急模式。解决方法是加nofail选项或者在启动参数里加systemd.unitmulti-user.target跳过。第四个是 rootfs 的 inode 里保留的 UUID 和实际分区对不上。fstab 里能用设备名就别用 UUID嵌入式设备的分区表经常被重新烧写。7. 集群侧的文件系统HDFS 和 GPFS 的操作差异7.1 HDFS 的命令操作与 POSIX 幻觉HDFS 的常用命令长得非常像 Linux这让很多人误以为它就是个大号 ext4。两者确实共享一套命令风格hdfs dfs -ls /user/test hdfs dfs -mkdir -p /user/test/data hdfs dfs -put local.csv /user/test/data/ hdfs dfs -get /user/test/data/local.csv hdfs dfs -du -h /user/test hdfs dfs -count -q /user/test hdfs dfs -chmod 755 /user/test/data hdfs dfs -setrep -w 3 /user/test/data/local.csv但底层模型差得远。HDFS 是一次写入多次读取的不支持原地修改文件内容要改只能重写。它的块大小默认 128MB比 ext4 的 4KB 大了四个数量级所以小文件是它的天敌——每个文件在 NameNode 里占一份元数据内存几百万个小文件能把 NameNode 内存吃光。权限模型也是像但不完全一样。HDFS 支持 rwx 位和 ACL但不支持 setuid、setgidsticky 位支持有限。符号链接支持也受限默认关闭需要配置dfs.client.follow.links。如果你从本地文件系统往 HDFS 迁移脚本这几个差异必须提前确认否则会出现明明有权限却删不掉或者软链接失效的问题。-count -q这个参数组合值得单独说。它输出的不只是目录下的文件数和总大小还包括配额信息命名空间配额、空间配额、剩余额度。在共享集群上排查为什么写不进去的时候这一条命令比看十份配置文档都快。7.2 GPFS 更换磁盘的正确流程GPFS 现在叫 IBM Storage Scale在 HPC 场景里很常见。换盘这件事看着简单操作顺序错了可能导致数据重分布跑上几天。标准的换盘流程是这样的先确认盘的状态用mmlsdisk fs_name -L看磁盘状态用mmgetstate -a看各节点守护进程是否正常。然后停止该磁盘mmchdisk fs_name stop -d diskname。接着在系统层面识别新盘可能需要mmlsnsd确认 NSD 状态。再启动磁盘mmchdisk fs_name start -d diskname最后跑mmrestripefs fs_name -b做数据再平衡。这里最容易踩的坑是直接拔盘再插新盘。如果文件系统配了副本或者底层 RAID也许能自动恢复但如果没配数据就真的没了。另一个坑是在业务高峰期做重分布mmrestripefs会占用大量 IO直接把上层业务的吞吐拖垮。我的建议是安排在维护窗口并且提前用mmdf确认剩余空间充足因为在重分布过程中临时数据会额外占用空间。注意更换磁盘前一定要确认该磁盘上的数据有副本保护。用mmlsdisk -L看 failure group 信息用mmgetstate确认集群健康再动手。8. 常见问题速查表与避坑清单8.1 排查对照表现象大概率原因第一手排查命令VFS: Cannot open root deviceroot 参数错或设备未就绪dmesg、检查 root 和 rootwaitKernel panic: No init foundinit 缺失或无执行位file /sbin/init、ldd挂载共享目录报 lookup 失败文件名含非法字符检查源侧文件名写入后断电文件变 0 字节数据未落盘加 fsync 或调 dirty 参数脚本报 bad interpreter混入 CRLFfile 命令、sed 去 \r权限全部变成 777挂载 FAT 未设 umask重挂加 umask/uid/gidHDFS 写不进去配额满hdfs dfs -count -q板子启动卡在 U-Boot引导镜像位置错检查 64 和 16384 扇区8.2 几条用时间换来的经验第一条跨平台协作的文件名规则要写进规范并且用工具检查靠人自觉一定会出事。我现在的做法是在 CI 里加一个脚本扫描仓库里所有文件的路径命中非法字符直接 fail。第二条sync不是万能的它只保证调用的那一刻已经提交到页缓存的数据落盘不保证硬件缓存里的东西落盘。有些 eMMC 和 SSD 有自己的写缓存断电照样丢。真正严格的做法是用带 FUA 或者写屏障的接口或者干脆用带掉电电容的存储。第三条嵌入式调试一定要把 NFS 远程挂载配好。这个投入产出比极高一次配置能省下几十次烧写。配置的时候把 vers 写死把 prototcp 加上稳定性会好很多。第四条遇到文件系统层面的报错先做减法能 ls 吗能 cat 吗能创建吗三个动作的结果组合起来能迅速把故障定位在名字解析、元数据还是数据读取上。这个二分法我用得最多比一条条看日志快。第五条集群文件系统的操作一定要在测试环境先演练一遍。mmrestripefs、mmdeldisk这类命令没有 undo敲下去就是几小时的重分布。生产环境的每一次变更都值得先在同等规模的小集群上跑一次。最后分享一个我自己常用的小技巧在做任何涉及分区或者格式化的操作之前先用lsblk -f和blkid把当前状态完整记录一份到文本文件里。真出问题的时候这份记录能帮你回忆起原来每个分区是什么类型、什么 UUID比凭记忆猜靠谱得多。这个动作花不了一分钟但救过我至少两次。
上一篇/下一篇内容由系统自动关联
返回资讯列表 →