系列前两篇我们聊了磁盘分区、文件系统创建和日常挂载,这套基础操作多数人已经能上手。但这篇我们继续往深处走一层:为什么Linux能同时挂着EXT4、XFS、NFS、FAT32这么多种格式而不打架?开机时根文件系统是怎么一步步被挂载起来的?文件写了一半断电,到底会丢什么?这些问题的答案,都藏在VFS抽象层和内核的几个机制里。我会结合这几年在生产环境踩过的坑,把VFS架构、根文件系统挂载链路、sync与缓存回写、磁盘扩容和故障修复一次性讲透。想真正把Linux磁盘文件系统这块儿“吃干榨净”的,这篇内容应该能帮到你。
1. VFS:为什么Linux能同时容纳这么多文件系统
1.1 VFS到底在中间做了什么
很多人学文件系统,上来就看inode、超级块、目录项,结果越学越乱。我建议先往后退一步,把VFS(Virtual File System,虚拟文件系统)想成一个“调度前台”。
你可以把VFS理解成一台服务器前的取号窗口。用户进程说“我要读/etc/passwd”,不需要关心这个文件到底存在EXT4的块设备上,还是存在NFS服务器的远程硬盘里,它只需要把路径丢给VFS,VFS负责找到对应的真实文件系统驱动,把请求翻译成那个驱动能理解的格式,再帮用户把结果拿回来。这个“翻译”的过程,就是VFS的核心价值。
VFS本身不管理任何真实磁盘数据,它维护的是六类核心对象:超级块对象(super_block)、索引节点对象(inode)、目录项对象(dentry)、文件对象(file)、地址空间对象(address_space)以及文件操作表(file_operations)。其中最容易理解的是inode和dentry。inode保存文件的元数据,比如权限、属主、大小、时间戳,以及数据块位置;dentry是路径中的一个目录项缓存,比如/var/log这个路径会被拆成根目录的dentry、var的dentry、log的dentry,缓存起来之后再次访问就能跳过繁琐的逐级查找。
正因为VFS把所有文件系统都抽象成同一套接口,上层应用和运维工具才能做到“一视同仁”。df、ls、cp这类命令在面对不同文件系统时,行为完全一致。这也是Linux生态里那种“今天格式化一个分区为XFS,明天把它改成Btrfs,应用层几乎无感”的底气来源。
1.2 用几条命令直接观察VFS与真实文件系统
纸上谈兵没意思,实操里最直观的观察入口是/mproc/filesystems。这个虚拟文件会列出当前内核注册过的所有文件系统类型,你可以把它理解成内核对VFS说“我能接这几类活”。
cat /proc/filesystems输出里常见的有ext4、xfs、btrfs、vfat、ntfs3、nfs、cifs、tmpfs、overlay等。注意每个名字后面有的带nodev标记,表示它不需要块设备支持,比如proc、sysfs、tmpfs,这类是内存或虚拟文件系统。
再配合两个我经常用的查看命令:
mount | grep -E 'ext4|xfs' # 看当前挂载真实文件系统的参数 df -hT # 看文件系统类型、容量和挂载点如果想看某个真实文件系统的内部结构,可以用dumpe2fs。比如我想确认/dev/sdb1的inode数量、块大小、保留块比例,直接执行:
dumpe2fs /dev/sdb1 | head -40输出里能看到:
- 文件系统状态:clean还是errors
- 块大小:4096字节还是1024字节
- Inode count:总共多少个inode
- First block、Block count、Reserved block count
- 文件和目录的默认属主、UUID
这就是VFS层下、真实文件系统层上的“档案”。遇到磁盘空间还有但报“No space left on device”的情况,多半就是inode耗尽,这时候查dumpe2fs里的Inode count和无空闲inode的报错,就能快速定位。
1.3 不同文件系统的选型逻辑
既然VFS屏蔽了底层差异,那选型时就全靠真实文件系统本身的特性了。这些年我把主流场景摸了个遍,整理了一张选型表:
| 文件系统 | 适合场景 | 关键技术点 | 劣势 |
|---|---|---|---|
| EXT4 | 通用服务器、桌面系统、中小型数据库 | 日志、在线扩容、成熟稳定 | 单文件系统容量上限受限制,大规模元数据性能稍弱 |
| XFS | 大文件、高并发写入、视频存储、数据库日志 | 动态inode分配、高扩展性、并发性能强 | 缩容困难,误删恢复难度高 |
| Btrfs | 需要快照、压缩、校验和的场景 | COW写时复制、子卷、checksum | 复杂场景下偶有性能波动,运维门槛偏高 |
| VFAT/NTFS | 与Windows交换数据、U盘、移动硬盘 | 兼容性优先 | 无权限管理,易碎片化 |
| LittleFS | 嵌入式SPI Flash、ESP32等资源受限设备 | 掉电保护、磨损均衡、动态损耗均衡 | 不适用于大容量机械盘/SDD场景 |
生产环境里最常见的组合是系统盘EXT4、大数据盘XFS。如果只是个人用,EXT4一路到底也不会错,毕竟它的小文件随机IO表现和在线扩容能力对普通运维来说是最稳妥的。嵌入式那边,如果主控是SPI NOR Flash或者小容量NAND,LittleFS这类为MCU设计的文件系统更合适,特别是需要对抗意外掉电的场景。
2. 根文件系统的挂载链路:从开机到Shell
2.1 内核是怎么找到并挂载根文件系统的
不少刚入门的同学对“根文件系统挂载”的理解就是一句话:开机时系统会把/挂上。但真正的链路比这细得多。按下电源键后,BIOS/UEFI加载引导程序(GRUB2),GRUB2加载内核镜像vmlinuz和可选的initramfs,内核开始初始化各种子系统,然后在某一时刻挂载根文件系统。
问题来了:内核这时候自己不认识磁盘控制器驱动怎么办?常见的做法是把必要的驱动模块塞进initramfs里。initramfs是一个临时的小型根文件系统,里面放了存储驱动的ko模块、udev规则和一小套工具。内核先把这个临时根挂载成内存里的tmpfs,然后跑/init脚本,脚本负责加载真正的磁盘驱动、组装设备节点,最后通过switch_root或pivot_root把实际根文件系统切换过来。
在GRUB2的引导配置里可以看到内核命令行参数:
cat /boot/grub2/grub.cfg | grep 'linux16' | head -1输出一般是:
linux16 /vmlinuz-... root=/dev/mapper/cl-root ro ...这里的root=指定根文件系统设备。生产环境里我更推荐用UUID代替设备名,因为设备名可能因为盘序变化而改变,UUID却不会。比如:
root=UUID=6c7e8f9a-1234-4abc-9def-0a1b2c3d4e5f可以用blkid命令拿到各分区的UUID,然后修改GRUB配置或通过内核参数指定。这个习惯能避免很多“今天开机进不了系统,明天另一块盘变成sda”的诡异问题。
2.2 fstab和systemd是如何接管挂载顺序的
根文件系统挂上之后,后续其他所有分区的挂载由systemd接管,配置源头是/etc/fstab。每一行从左到右依次是:设备、挂载点、文件系统类型、挂载选项、dump位、fsck检查顺序。后面两个字段经常被忽略,但很关键。第六列的fsck顺序,1表示根文件系统先检查,2表示其他需要检查的分区,0表示不检查。
我遇到过一台机器因为fstab里多写了一个不存在的分区,开机直接卡在A start job is running for dev-disk-by的等待环节,要等90秒才超时继续。排查方法就是在系统参数里加systemd.unit=emergency.target进入紧急模式,把fstab里那行注释掉。
针对这种“网络盘不想拖垮开机”的情况,挂载选项里建议加上_netdev和nofail。_netdev告诉systemd这个设备需要网络就绪后才挂载,nofail表示挂载失败不阻塞启动流程。NFS和CIFS这类远程文件系统尤其需要这两个选项。
如果想看开机阶段哪些挂载点拖慢了速度,可以执行:
systemd-analyze blame | sort -k2 -n | tail -10它能列出每个单元花了多久,挂载慢的单元一眼就能看到。
2.3 嵌入式场景中的NFS根文件系统和LittleFS
嵌入式开发和服务器场景有些不同。开发阶段最舒服的调试方式,是把根文件系统放在开发机上,通过NFS挂载给目标板。目标板的内核命令行看起来类似:
root=/dev/nfs nfsroot=192.168.1.100:/opt/rootfs,v3,tcp ip=dhcp关键点是什么呢?是网络必须能通,且NFS版本建议明确指定v3或v4。热词里出现“嵌入式linux 根文件系统挂载 使用nfs v3”,说明很多人在实际开发中确实会用到v3,因为有些板子的NFS v4实现不完整,挂载后各种莫名权限问题。用v3时,要在开发机的/etc/exports里加上类似下面这行:
/opt/rootfs 192.168.1.0/24(rw,sync,no_root_squash,no_subtree_check)其中no_root_squash很关键,它允许目标板的root用户拥有和开发机root相同的文件权限,否则交叉编译出的环境一上去全是权限错乱。
而对于flash存储,如果用的是littlefs,通常在应用层通过mbedTLS和FAL抽象层接入,比如在STM32或ESP32上读写文件。要注意的是,littlefs的块大小必须和Flash扇区对齐。我踩过坑:块大小设置过大,导致格式化时剩余空间利用率奇低。正确做法是看器件手册确认扇区大小,比如W25Q128是4KB扇区,那littlefs的block size就设为4096。
3. 磁盘管理实操:格式化、分区、扩容
3.1 从裸盘到可用分区的完整流程
新买一块盘,或虚拟机里加了一块新磁盘,常见路径是:识别磁盘、分区、格式化、挂载。先用lsblk查看盘符:
lsblk --ascii假设识别到/dev/sdb,可以用fdisk做MBR分区,或者parted做GPT分区。现在大于2T的盘建议直接用GPT格式,因为MBR的2T限制会卡死你。fdisk创建新分区的交互步骤常规操作是有问必答:输入n新建分区,选择主分区p,指定起始扇区(默认即可),指定结束扇区(如果只要一个区,直接回车),最后输入w写入。如果不想交互,parted更适合脚本化:
parted -s /dev/sdb mklabel gpt parted -s /dev/sdb mkpart primary 1MiB 100%注意起始位置留1MiB,是为了对齐现代磁盘的4K扇区并且为GRUB保留元数据空间。格式化就看需求,XFS和EXT4二选一:
mkfs.xfs -f /dev/sdb1 mkfs.ext4 /dev/sdb1格式化之后千万别着急挂载,先把UUID记录下来,后面写fstab要用的:
blkid /dev/sdb1挂载和写入fstab:
mkdir -p /data mount /dev/sdb1 /data echo 'UUID=xxx /data xfs defaults,noatime 0 2' >> /etc/fstab从这条命令里能看到我习惯加noatime,减少每次读文件都更新访问时间戳的IO压力,对高并发读写场景有明显的性能改善。
3.2 扩容:Ubuntu根分区和VirtualBox磁盘加空间
“ubuntun磁盘扩容”是搜索热词,说明很多人卡在根分区空间不足。我梳理两种常见路径:一种是用LVM的情况,一种是没有LVM、直接普通分区的情况。
如果当初安装系统时用的是LVM,扩容相对优雅。先在虚拟机层面扩大磁盘,然后在系统里执行:
echo 1 > /sys/class/scsi_device/0:0:0:0/device/rescan # 让内核重新识别磁盘新容量 pvresize /dev/sda2 # 扩展物理卷 lvextend -l +100%FREE /dev/mapper/ubuntu--vg-ubuntu--lv # 把所有剩余空间加入逻辑卷 resize2fs /dev/mapper/ubuntu--vg-ubuntu--lv # EXT4在线扩容如果用XFS,最后一步要换成xfs_growfs /挂载点,一定不能用resize2fs,否则直接报错。
如果没有LVM,普通分区扩容就麻烦一些。一种思路是用growpart在线扩展分区:
growpart /dev/sda 2 resize2fs /dev/sda2growpart把分区表里的结束扇区扩展到磁盘末尾,然后再在线扩文件系统。这条路通常要求后面没有未分配空间阻挡,如果有,就得先把分区往后挪,风险就上来了。建议生产环境提前用LVM,避免这种痛苦。
至于VirtualBox给虚拟机加空间,操作顺序是:先在VirtualBox管理器里调整vdi/vmdk大小,然后进虚拟机执行rescan或者重启。很多人在这一步没让内核重新识别磁盘容量,导致growpart报“磁盘没有变化”的错。rescan作为虚拟化环境下的标准操作,能减少重启次数。热词里出现的“system占用100磁盘”“vbox磁盘扩容”,大概率就是把rescan这步漏了。
3.3 挂载选项对IO性能的影响
很多人挂载时一路defaults到底,但对数据库、文件服务器这类业务,挂载选项不同,表现差距能超过30%。我重点说几个:
- atime/noatime/relatime:atime每次读文件都更新访问时间,造成大量写IO;relatime仅在访问时间早于修改时间或过去24小时才更新,是Linux默认策略;追求性能就上noatime。
- data=ordered/data=writeback:EXT4的日志模式。ordered保证数据先于元数据写盘,一致性更好;writeback只记录元数据,快一点,但异常掉电可能内容错乱。
- discard/nodiscard:SSD支持TRIM时可以用discard,让SSD回收空闲块;但如果底层RAID卡不支持转发TRIM,反而有性能坑,我一般开着定期fstrim而不是挂载选项里直接开。
例如数据库数据盘我会挂成:
mount -o noatime,nodiratime,data=ordered /dev/sdb1 /dataSQLite、MySQL这类应用对数据一致性要求高,别为了那点性能牺牲掉data=ordered。
4. 数据一致性:sync、缓存回写与掉电真相
4.1 写文件不是立刻落盘的
这是新手最容易误解的点。你在Linux里执行cp或echo写入文件,数据首先进入页缓存(page cache),内核中的回写线程(writeback)会在后面某个时间点把脏页(dirty pages)刷到磁盘。这种机制极大提升了写入吞吐,但它也意味着“你以为写完了,其实还没落盘”。
查看当前系统有多少脏页可以看/proc/meminfo:
grep -i dirty /proc/meminfo输出中的Dirty项就是内存里等待回写的脏页数量。Writeback项是正在写回硬盘的数量。
当业务要求写完必须可见、持久化,就要用sync系列命令。它们之间区别很大:
- sync:把所有挂载文件系统的脏页都刷下去。
- fsync(fd):把指定文件的脏数据和元数据都刷下去,适合单个文件。
- fdatasync(fd):只刷数据,不刷元数据,比fsync稍快。
在shell里操作时,你经常跑的其实不是直接的系统调用,而是会用到sync命令。比如复制了重要文件后执行:
sync这在“拔U盘之前先sync”的场景最典型,虽然现代桌面系统拔U盘时有自动卸载流程,但服务器上养成同步后关机、同步后卸载的习惯,能避开很多奇怪的数据丢失问题。
4.2 回写参数怎么调才对
内核回写相关的参数集中在/proc/sys/vm/下,关键的有四个:
sysctl vm.dirty_ratio sysctl vm.dirty_background_ratio sysctl vm.dirty_writeback_centisecs sysctl vm.dirty_expire_centisecsdirty_ratio表示当脏页占内存比例达到这个值后,进程自己的写入会被阻塞,强制同步回写。dirty_background_ratio则是内核后台回写线程开始刷盘的阈值。这两个值的关系可以理解成:到了background水位,内核默默开始刷;到了dirty水位,不好意思,你的写操作得排队等盘写完。
默认情况下dirty_ratio是20,dirty_background_ratio是10。如果你机器内存很大,比如256G,20%就是51G,这意味着系统最多允许51G脏页悬在内存里,一旦达到,写入瞬间卡顿。对数据库这类场景我通常会把这两个值调低一些,比如10和5,宁可多刷几次,也要控制突发延迟。反过来,在纯文件导出、写大批量日志的场景,可以稍微调高,减少磁盘seek次数。
修改方式顺手写一下:
sysctl -w vm.dirty_background_ratio=5 sysctl -w vm.dirty_ratio=10 echo 'vm.dirty_background_ratio=5 vm.dirty_ratio=10' >> /etc/sysctl.conf还有一个东西要提醒:日志文件系统(EXT4/XFS)并不是不丢数据,而是把元数据和数据提交的顺序做了保护,避免掉电后文件系统结构不可用。但应用层没调fsync的数据,仍然可能丢。想完全不丢,要么每次关键写操作都fsync,要么让数据库开双写并设置synchronous_commit,使用体验像拿性能换信仰。
4.3 磁盘突然只读的排查思路
“磁盘全盘只读”这个现象,在虚拟化平台和物理机上都有可能遇到。根因基本分四类:
第一种是文件系统检测到错误后主动转为只读,比如EXT4的“errors=remount-ro”默认策略。dmesg里能看到大量EXT4-fs error字样。处理方法是先排查IO错误,执行:
dmesg | tail -50 journalctl -k -b | grep -i error如果只是文件系统内部不一致,可以卸载后fsck修复;但如果看到blk_update_request: I/O error、Buffer I/O error这类硬件层面的错,先查线缆、硬盘健康状态,别急着反复挂载。
第二种是虚拟化平台层面的磁盘锁或全盘只读,比如某些Hypervisor的热迁移、快照合并过程中,磁盘被置为只读。这时候宿主机上重置磁盘状态或暂停虚拟机再恢复,往往就能解除。
第三种是挂载参数里带了ro,结果把只读写进了fstab。这种最冤枉。排查方法很简单,执行mount看看有没有ro标识,再看/etc/fstab对应行。
第四种是文件系统本身没问题,但上层服务或系统盘满了之后,某些程序没处理好错误,把运行状态误写成“只读”。比如VDO或条件性只读的overlayfs,会把底层写满导致的错误放大。
遇到只读第一步永远不要盲目重启,先确认是硬件还是文件系统层问题,否则可能导致更严重的文件系统损坏。如果根分区已经只读,但还能操作,可以先试着重新挂载:
mount -o remount,rw /能成功说明文件系统层问题不重;不成功就老老实实进急救模式排查。
5. 文件系统坏损与误删恢复
5.1 fsck和xfs_repair的正确操作姿势
EXT4和XFS各有各的修复工具。EXT4是fsck家族,XFS是xfs_repair。核心前提都一样:先卸载,再修复。对着一个挂载中的文件系统跑fsck,后果可能比坏损本身更严重。
umount /dev/sdb1 fsck -y /dev/sdb1-y的意思是自动回答yes,适合排查阶段。但要注意,fsck过程中出现“Recovering journal”是正常的,它在重放日志、处理未完成的元数据操作。如果fsck卡在“ optimizing extent trees”很久,一般是文件系统比较大或者损坏范围广,要给足耐心。
XFS修复略有不同。XFS不支持在挂载状态下直接修复,但它的日志重放和结构修复通常是自动的。如果挂载失败,需要执行:
xfs_repair -L /dev/sdb1这里的-L是“清空日志并强制重建”。注意:-L会丢弃文件系统日志,未完成的数据写入会受影响,所以不到万不得已不要用。xfs_repair跑的时候也会打印很多进度,比如“Phase 1 - find and verify superblock...”,每个Phase都有含义,Phase 4和5是检查inode和路径结构,耗时最长。
5.2 突然断电后文件系统为什么会变得“可恢复”
很多人问:为什么Ext4/XFS断电后一般能很快挂载,而FAT32/U盘坏一次就满地找牙?关键在于日志(journal)机制。你可以把它理解成记账本:文件系统在修改元数据之前,先把“我要改哪些块”记到日志区,改完后做提交记录,最后等数据稳定再清除日志。断电时如果日志区有未完成的事务,挂载时重放一下就好,磁盘结构依然是完整的。
裸奔的FAT32没有这层保护,断电时目录项和FAT表可能停在半修改状态,就会出现文件“存在但打不开”、目录结构错乱。
因此,生产环境里我从不建议在关键数据盘上关闭日志,即使EXT4的nojournal模式能减少一部分写放大。省了日志,结果就是掉电后多花几小时做fsck,甚至直接丢数据。
5.3 误删文件后的黄金抢救期
误删文件是每个用Linux的人迟早要面对的事。先说结论:能不能找回,取决于两个因素——文件系统是否还在被写入,以及你用的是EXT系列还是XFS。
对于EXT4,删除文件只是把inode里的链接数清零并释放数据块,数据块里的内容还没被覆盖,所以理论上可以用extundelete:
umount /dev/sdb1 extundelete /dev/sdb1 --restore-file /path/file --restore-dir /恢复目录越早操作越好。文件一旦被新数据覆盖,神仙难救。XFS因为动态inode设计,误删恢复比EXT4难很多,基本没有公开可用的成熟方案,只能靠快照或备份恢复。
我的实操习惯是:服务器上的关键目录,要么做LVM快照,要么定期rsync到备份盘。日常操作用rm -rf之前,先ls -ld确认路径,再确认变量不为空。别觉得这个提醒多余,我见过太多把脚本里的/home/${user}写成/home/${user:?}没加,或者是rm -rf /usr /lib这类事故,变量一旦为空,就删到根目录去了。
5.4 文件系统层面排查高磁盘占用
热词里有“system占用100磁盘”,这个更多是Windows场景,但Linux里也常出现磁盘IO跑满或空间被占满的情况。排查时我建议按顺序看四样东西:
df -hT # 空间占用 dv -iT # inode是否耗尽 iostat -x 1 # 看每秒读写、await、util iotop # 定位是哪个进程在疯狂IO有一个场景很坑:某个进程删除了一个大日志文件,但进程没释放文件句柄,磁盘空间依然被占着。df显示满了,但实际上删除空间被“占用中”,这时候用:
lsof +L1可以列出所有被删除但仍打开的文件。看到确实存在后,重启对应进程或kill掉,空间自然就释放了。这个经验在排障时能省不少时间。
6. 高频问题实录:扩容、外部盘、卷信息与后台任务
6.1 磁盘动态外部与找不到卷信息怎么处理
Windows或虚拟化平台下的“磁盘动态外部”现象,本质上是动态磁盘和基本磁盘之间的格式差异。放到Linux语境下,就是分区表类型的问题。如果一块动态磁盘带到了Linux上,lsblk可能看不到可用分区,或者fdisk无法识别PARTTYPE。稳妥的做法是确认没有重要数据后,重写分区表为GPT或MBR,或者转回基本磁盘。在Linux里重写分区表的命令参考前面第3章,注意期间一定不要动其他盘,避免选错设备。
“找不到这个磁盘的卷信息”或者Windows报“磁盘必须经过初始化,逻辑磁盘管理器才能访问”,大意是磁盘上没有有效的分区表。这可能是因为磁盘被从某种MD设备或LVM里分离出来了,也可能是分区表损坏。Linux下可以用:
fdisk -l /dev/sdb parted -s /dev/sdb print如果没有输出分区信息,但盘本身有数据,不要急着初始化,先用testdisk扫描分区表,它也许能帮你恢复出丢失的partition记录。如果确定盘里没有数据,直接新建分区表就行。
6.2 虚拟化磁盘扩容与Windows磁盘检查问题
VirtualBox给虚拟机加空间,之前只讲了rescan那步,这里把完整顺序再补一遍:先关闭虚拟机,在VirtualBox主界面设置里把vdi或vmdk大小拉大;如果用的是vmdk并且之前是固定大小(fixed),它可能不允许直接扩容,要先克隆为动态大小或者用VBoxManage转换。启动虚拟机后,执行scsi rescan或重启,然后lsblk确认新容量,再走growpart和resize2fs。
另外热词里有一句“由于该卷已设置为写保护,因此Windows无法在上面运行磁盘检查”,这是Windows常见的卷写保护陷阱。Linux里如果遇到真的写保护卷,先看看是不是硬件锁(U盘侧面的滑动锁),再看挂载选项里有没有ro。Windows里的写保护卷在Linux上有时能强制挂载:
mount -o remount,rw /dev/sdXn但根因如果是设备本身进入了只读保护模式,那得上硬件检测工具看健康状态了。
6.3 后台任务与运行Windows程序的Linux姿势
热词里还有两个常用问法:“linux 让后台运行指令 不因界面退出而退出”“linux运行windows程序”。前者是进程会话管理问题,nohup是基础方案:
nohup long-running-command > /tmp/log 2>&1 &但nohup只是把进程对挂断信号的响应忽略掉,更完整的方案是用tmux或screen,特别是你还需要重新连回去看输出的情况:
tmux new -s work tmux attach -t work这在运维操作中非常常见:远程SSH执行一个长任务,比如磁盘分区转换、大文件复制、fsck,一旦会话断掉任务跟着死,用tmux包一层,断线重连也能继续看到现场。
至于运行Windows程序,常见路线是wine和Windows虚拟机。wine适合轻量级工具软件,命令行工具配合winetricks配置也能跑一些东西。重型应用或者需要直接操作磁盘、硬件的场景,老老实实装个qemu或VirtualBox虚拟机更靠谱。但要注意,除非是明确定位的测试环境,别在生产服务器上为了跑一个Windows小工具而引入不必要的兼容层,安全性和稳定性都得不偿失。
7. 实战心得:磁盘与文件系统,功夫要下在日常
最后分享一点我个人的体会。干了这么久运维和嵌入式开发,我越发觉得磁盘文件系统这块儿的功夫不在出问题的那个瞬间,而在于日常有没有把“数据落盘”当成一个严肃主题来对待。sync这个命令看似简单,但真出事时能救你命。我习惯在每次执行完重要数据操作后,手动sync一次,把磁盘卸载前sync,把U盘拔出前sync,这种“强迫症”让我避开了很多起数据不完整的事故。
有一次在生产机的根文件系统报只读,我没急着重启,先去dmesg里翻了半天,发现是底层RAID卡的一块盘坏道触发了系统remount-ro。冷静分析后把服务流量切走,在维护窗口里更换硬盘并fsck,整个恢复过程非常平稳。回头想想,如果当时直接重启生产机,文件系统可能带着错误反复挂载,后果不堪设想。
另外,日常建议为所有重要文件系统开启定时快照。LVM快照、snmappshot这类工具都成熟,快照成本不高,但恢复的时候就是救命稻草。文件系统不是玄学,它的行为有明确逻辑可循,只要你多花一点时间理解VFS、感知writeback、学会读dmesg,遇到故障时就不会慌,Linux的磁盘和文件系统,说到底是可以被稳稳拿捏的。