
一块全新的硬盘被塞进服务器到你能在终端里对着ls /data看到目录中间发生的事情远比表面上那几条命令复杂。很多人用 Linux 两三年lsblk、fdisk、mkfs、mount都敲得飞快可一旦遇上“盘明明认到了但系统看不到”“重启之后挂载失败”“df 显示满了可 du 查不到大文件”这类经典故障照样会卡很久。原因不在于命令不熟而在于脑子里缺了一条从硬件硬盘到文件系统的完整链路。这篇文章我打算把这条链路从下到上逐层拆开讲透顺带把我在生产环境里踩过的、见过别人踩的坑一并交代出来。适合刚入门想建立整体认知的 Linux 学习者也适合干了两年依然只能靠背命令应付面试的运维朋友。1. 先搭建全链路视图硬盘到文件系统之间到底隔了几层1.1 用 lsblk 和 df 先看清你的存储布局拿到一台 Linux 服务器别急着翻文档先敲两个命令看门道。lsblk这是我随便搭的一套虚拟化环境下常见的输出NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINTS sda 8:0 0 100G 0 disk ├─sda1 8:1 0 1G 0 part /boot └─sda2 8:2 0 99G 0 part / sdb 8:16 0 2T 0 disk └─sdb1 8:17 0 2T 0 part /data nvme0n1 259:0 0 512G 0 disk └─nvme0n1p1 259:1 0 512G 0 part再看一眼df -hTdf -hT输出会是另一副样子只列出已经挂载好的文件系统比如/dev/sda2 ext4 /、/dev/sdb1 xfs /data。这两个命令放在一起看就是整条链路的静态快照sdb是一块物理磁盘节点它上面长出一个分区sdb1分区内建好了文件系统文件系统被挂载到了/data。lsblk展示的是设备与分区的关系df展示的是文件系统与挂载点的关系。很多新人把这两个命令混为一谈其实它们站在链路的不同高度。df根本不关心底层是 SATA、NVMe 还是网络存储它只负责回答“已经挂载的文件系统用了多少、还剩多少”。1.2 五层链路每一层只回答一个问题我把这条链路拆成五层每一层只回答一个专门的问题设备节点层/dev/sdb让内核和用户态都知道“有一块物理硬盘存在”它是驱动与用户态之间的接口。分区表层sdb1、sdb2回答“这块盘被切成了几个逻辑区域”每个区域有明确的起始位置和长度。文件系统层ext4、XFS回答“一个区域里的数据字节按什么规则组织成文件和目录”。挂载点层/data把文件系统接到系统全局目录树上让它对用户可见。VFS 层向上层应用屏蔽各文件系统与底层存储的差异提供统一的open/read/write接口。打个比方硬盘是毛坯房分区是砌墙隔间文件系统是往隔间里装书架、衣柜、贴门牌挂载是开门迎客VFS 是物业统一管理。毛坯房不能直接住人光有隔间也不能住必须装修到能用的状态——文件系统就是这个“装修标准”。掌握这条链路之后排障思路会清晰很多。遇到问题第一个动作永远是确认问题出在第几层df看不到某块盘先看lsblk有没有识别到分区lsblk能看到盘和分区再看mount有没有挂载全都挂上了还读写慢才轮到排查驱动、线缆、RAID。你一旦开始用这种分层视角看问题那些看似玄学的故障大概率会变成一道简单的判断题。2. 设备识别层一块物理硬盘是如何变成 /dev/sdb 的2.1 设备文件不是文件是驱动和用户态的握手通道很多对/dev/sda有误解以为它是“硬盘的镜像文件”。不是。它是一个入口是 Linux“一切皆文件”哲学在设备层面的体现。你在用户态读写/dev/sdb实际上是经过 VFS、走进内核的块设备驱动驱动再跟硬件控制器打交道。设备文件靠主设备号和次设备号来标记自己指向谁。ls -l /dev/sda会看到类似8,0的数字/dev/sdb是8,16。主设备号决定内核加载哪个驱动次设备号在这个驱动下区分具体是哪一块盘。老一点的工程师可能还记得当年没有 udev 的时候设备节点要靠mknod手动创建号码写错一个字节盘就成了另一块盘。那是个真正的黑暗时代现在这套动态机制已经把这些细节全部藏起来了。2.2 命名规律与 udev 动态机制现代 Linux 里/dev下的节点不是开机时就写死在磁盘上的而是udev根据内核上报的 uevent 动态创建的。命名规则看起来乱实际上很有规律sd*SATA、SCSI、USB 等走 SCSI 命令集的盘sda是第一块sdb是第二块按被内核扫描到的顺序排。nvme0n1NVMe 盘0是控制器号n1是 namespace 序号它上面的分区显示为nvme0n1p1。vd*QEMU/KVM 虚拟机的 virtio 虚拟盘。mmcblk*eMMC、SD 卡。看到名字就能猜出硬件类型这个能力在日常排障里很值钱。想深入看某块盘的信息用udevadm可以挖出很多底料udevadm info --queryall --name/dev/sdb | head -30输出里包含设备路径、供应商、序列号等信息。而ls -l /dev/disk/by-uuid/则能展示当前系统所有分区的 UUID 与真实设备节点的对应关系。2.3 设备名漂移为什么 fstab 必须写 UUID真正容易踩坑的是 udev 按发现顺序命名这件事。一台机器插了一堆 USB 盘或者换了 SATA 盘位开机后sdb可能变成sdc原来sdc变成了sdb。移动硬盘、U 盘、多盘位 NAS 这种场景最容易中招。我之前帮人处理过一个 NAS两块盘做了 RAID1另外还有一个外置备份盘。某次重启之后备份脚本开始往系统盘里写数据。查到最后就是因为脚本里挂着/dev/sdb而外置盘和内置盘的枚举顺序变了。从那以后我所有挂载配置里只写 UUID设备名拿去给人看不拿去给机器用。顺带说一句很多人卡在“盘认没认到”这个问题上。判断的第一选择永远是lsblk而不是df。df只显示已挂载的文件系统lsblk显示的是内核视角的设备树。别小看这个区别它能帮你快速确认问题到底出在“没认到盘”还是“认到了但没挂上”。3. 分区层MBR 与 GPT 选择背后的物理与逻辑约束3.1 分区表是磁盘的目录页MBR 与 GPT 怎么选设备节点让内核知道有一块盘但盘的物理空间是一整块连续的字节序列。分区表做的事情就是在这块字节序列上画格子哪个区间是分区 1哪个区间是分区 2每个分区的类型、起始扇区、长度各是多少。它相当于一本书的目录页。注意分区表本身也存在盘的最前面而不是存放在操作系统里——这也是为什么分区表一旦损坏整块盘看起来就像“空盘”。MBR 和 GPT 是两种完全不同的目录格式核心差异看这张表对比项MBRGPT容量上限约 2TB按 512B 扇区理论上限极大实际远够用主分区数量最多 4 个默认 128 个可扩展备份机制无损坏即全丢盘尾有备份表 CRC 校验引导方式BIOS/LegacyUEFI也可兼容 Legacy兼容性所有老系统新版系统与 UEFI 环境标配现代新机器直接选 GPT尤其是超过 2TB 的盘、以及要装 UEFI 引导的机器。MBR 在盘超过 2TB 后会变得非常尴尬——多出来的空间不是不能分而是分区表根本表达不了。别硬扛换 GPT 是正路。3.2 parted 实操从 1MiB 对齐开始如果只是想快速把一块新盘分一个分区出来parted的非交互模式最不容易出错parted -s /dev/sdb mklabel gpt parted -s /dev/sdb mkpart primary 1MiB 100%第一条把盘打成 GPT第二条从 1MiB 开始分一个主分区到盘尾。为什么从 1MiB 开始而不是从 0现代硬盘的扇区早就不止 512BSSD 的内部写入单元是页和块RAID 阵列还有条带宽度。分区起点落在 1MiB 这种对齐位置能避免一次 IO 横跨两个物理单元性能和寿命都能改善。fdisk交互界面里默认的起始扇区是 2048 扇区也就是 1MiB背后的道理完全相同。分完别急着走。内核并不会马上使用新分区表partprobe /dev/sdb lsblkpartprobe让内核重新读盘头看到新的分区布局。如果有分区正在被使用它会报target is busy那就得先卸载或停掉持有者或者干脆重启再验证。如果你需要的是整块盘不分区就直接让文件系统用也不是不行直接把裸设备mkfs就好。但我会建议先分区再格式化原因有两点一是后续无法再安全地增加分区二是很多运维工具和监控脚本默认“磁盘 - 分区 - 文件系统”三层模型裸设备会让它们识别混乱。除非你有明确理由比如整个盘要交给 LVM 物理卷或 ZFS zpool。3.3 分区策略系统盘、数据盘与常见误区系统盘和数据盘的分区策略截然不同。系统盘要兼顾引导和系统目录通常包含/boot或 EFI 分区、/、swap老派做法还可能分/home、/var好处是某个分区写满不影响其他分区数据盘则简单得多一个大分区或者按业务拆几个分区就完事。还有一个反直觉的点分区不是越多越好。分区越多空间利用率越低管理越碎。你要考虑的是故障隔离和备份策略而不是把一块盘切成十几个区展示规划能力。另一个常见误区是“先 mkfs 再调整分区”。文件系统一旦建好它内部假设了分区的起始位置、大小、对齐方式。你用分区工具把分区前移或缩小文件系统还在原地轻则空间浪费重则元数据损坏。正经做法是先规划好分区再格式化实在要改先备份数据、重新分区、再重新格式化。gdisk还支持给整个分区表做备份sgdisk --backuptable.bak /dev/sdb这个习惯在折腾生产盘之前非常值得养成。分区表备份不等于数据备份但它能让你在误操作后至少回到操作前的分区布局。4. 格式化层mkfs 不止是“把盘清空”4.1 文件系统是自洽的数据库超级块、inode、dentry 与数据块很多人把mkfs理解成“清空磁盘”这是大错。mkfs的本质是在一个分区上建立起一套完整的元数据体系让这块区域可以用“文件”和“目录”的形式管理数据。可以把它看作一个自洽的迷你数据库几个关键组件超级块superblock整个文件系统的总账本记录块大小、总块数、状态、日志位置等全局信息。ext4 还会在多个块组各放一份超级块备份主超级块坏了可以用备份救。inode每个文件/目录一张“身份证”存权限、属主、大小、时间戳和指向数据块的指针。注意inode 里没有文件名。目录项dentry负责把文件名映射到 inode。所以mv重命名文件只改目录项inode 和数据块纹丝不动。数据块data block真正存文件内容的地方按块大小默认 4096 字节切分。这套设计决定了你的很多运维直觉。目录里文件数量巨大时瓶颈往往在 dentry 和 inode 分配上文件特别小时inode 和数据块都占着开销比内裤还高小文件超多的场景格式化时就要提前放大 inode 数量。4.2 mkfs 实战ext4 与 XFS 的参数选择正式动手之前一个必须执行的动作lsblk、blkid确认你接下来mkfs的目标到底是不是你要操作的那块盘。过去这些年我见过不止一次把数据盘格式化到系统盘上的事故多数不是技术不行是凌晨两台机器同时操作时瞄错了盘符。把盘认清楚再敲命令。ext4 的通用做法mkfs.ext4 -L data -b 4096 -m 2 /dev/sdb1-L给文件系统打卷标-b设定块大小默认 4096 不要乱动-m是给 root 预留的块比例默认 5%对超大容量盘偏高可以调到 2% 左右但别设成 0。文件系统在做碎片整理和 resize 时需要这个余量。XFS 的做法mkfs.xfs -f -L data /dev/sdb1-f用于强制重建。XFS 不提供像 ext4 那样的预留块比例参数它的元数据结构本身对大规模部署更友好。格式化完成后用blkid确认blkid /dev/sdb1它会输出 UUID 和文件系统类型这串 UUID 才是后面挂载血缘里真正的身份标识。ext4 和 XFS 怎么选我的经验是默认场景、文件数量大但单文件体积不大、可能需要用resize2fs做在线扩容、或者你的发行版默认就是 ext4选 ext4。大文件、高并发、单文件吞吐要求高比如数据库数据目录、大数据节点选 XFS。XFS 最大的缺点是不能缩容分区空间规划时必须一步到位否则只能重做。4.3 格式化后空间去哪了元数据的真实开销新盘 1TB格式化完成之后df -h一看可用空间只剩九成出头很多新人第一反应是盘有问题。其实这笔账很好算。首先是单位换算盘厂按 1000 进制标容量系统按 1024 进制显示1TB 盘本身就只有约 0.91TiB。其次是元数据开销inode table 按每个 inode 256 字节、每 16KB 数据一个 inode 来算1TB 盘大约会生成 6000 万个 inode仅 inode 表就是十几 GB。再加上日志区、块组描述符以及 ext4 默认的 5% 预留块总共少掉 5% 到 10% 完全正常。XFS 同样有元数据开销只是结构不同。这不是故障是文件系统的运行成本。另一个分支值得提一句单片机和嵌入式场景里的裸 Flash传统 ext4/XFS 这套太重littlefs 这类为小容量、掉电不稳定存储设计的轻量文件系统更合适。它们同样完成“从裸设备到文件系统”的过渡只不过把 inode 表换成了以块为单位的日志式元数据管理弱电环境里也不容易丢数据。理解了 PC 上这条链路再看 littlefs 的设计思路会非常顺。5. 挂载与 VFS文件系统真正对外可见的时刻5.1 挂载的本质把独立树接入全局目录树分区上有了文件系统但用户还看不见它。文件系统本身是一棵独立的树有自己的根。挂载做的就是把这棵树的根接到系统全局目录树的一个指定目录——挂载点上。挂载完成之前你在挂载点目录里看到的是原目录的内容挂载之后原目录内容被“遮盖”你会看到新文件系统的根。这也是一个经典面试点你往一个非空目录挂载文件系统原来的数据并没有丢umount之后原内容还在原处等着你。实操命令很简单mount /dev/sdb1 /data findmnt /data mount -o remount,rw,noatime /datafindmnt看挂载关系比mount更直观能直接显示树形层次推荐大家多用。5.2 fstab 配置与挂载参数的取舍重启后要让系统自动挂载就得写到/etc/fstab。六列分别指定设备、挂载点、文件系统类型、挂载参数、是否参与 dump 备份、fsck 检查顺序。现代 Linux 里我推荐写成这样UUIDxxxx-xxxx /data xfs defaults,noatime 0 0设备列必须用 UUID 而不是/dev/sdb1理由前面讲过。参数列里noatime值得默认加上它让系统不再每次读文件都更新访问时间减少不必要的写盘对数据库、日志类负载很友好。SSD 上有人加discard开启 TRIM但每次删除都下发 TRIM 会有性能影响更稳的做法是挂载时不加discard定期用fstrim做批量回收。dump 和 fsck 两列对 XFS 分区直接写0 0就行因为 XFS 不做开机自动检查对 ext4 根分区习惯写0 1其他 ext4 分区写0 2。写完 fstab 先别重启执行mount -a验证语法和挂载是否成功。如果因为 fstab 写错导致开不进系统在 GRUB 菜单里临时追加systemd.unitrescue.target或者用 LiveCD 启动后把 fstab 里出错行注释掉——这是每个运维都应该会的基本动作。嵌入式开发里还有一类特殊挂载让人印象深刻根文件系统不放在本地盘而是通过 NFS 挂载。u-boot 传root/dev/nfs nfsroot服务器IP:/path,tcp,v3内核启动时把远端目录挂成/。开发期改完代码直接生效不用反复烧写 Flash。它跟本地盘唯一的区别在于底层块设备变成了网络协议上面的流程一模一样。这也正好引出 VFS 的价值。5.3 VFS统一一切“像文件”的东西VFS虚拟文件系统是 Linux 文件系统的总调度室。应用层调open/read/write时根本不需要关心目标文件在 ext4 上还是 XFS 上在本地盘还是 NFS 上。VFS 用四个核心对象把它们包成统一形态super_block描述一个已挂载文件系统inode描述一个文件对象dentry描述路径中的一个目录项file描述一个已打开的文件描述。设备文件、proc、sysfs、tmpfs在 VFS 眼里都是文件系统只是底层不做磁盘寻址而已。这也是“一切皆文件”能成立的技术底座。所以回到链路来看VFS 是向上的一层它把前面所有层级的差异吸收掉让普通程序面对一个统一的“文件世界”。这也是为什么df看不到没挂载的盘、mount能看到已挂载的盘、lsblk能看到物理层的盘——这三个工具恰好站在链路的不同高度。6. 从 write() 到落盘sync、页缓存与崩溃恢复的真相6.1 write() 只是写进了页缓存很多新手以为调用write()数据就写进硬盘了。实际上大多数场景里write()只是把数据复制进了内核的页缓存page cache把页面标记成 dirty然后立刻返回“成功”。真正把脏页刷到盘上的是内核的后台回写线程它们按比例、按时间窗口批量刷盘。为什么要这样设计为了性能。顺序批量写远比每次一字节一字节落盘高效这也是 Linux 在大量小文件写场景下性能仍然能看的原因。代价是掉电风险应用以为成功了数据还在内存里断电就没了。类比一下就是你写作业先打草稿草稿还没誊到正式本子上就停电了——草稿本还在但正式本子没有内容。6.2 sync、fsync 与内核回写节奏sync命令做的事是把当前所有 dirty 页排队刷到磁盘是运维人员常用的“尽量落盘”手段。但如果你在意的是某一个文件的完整性比如数据库的 WAL 日志那要用fsync()或fdatasync()它们保证指定文件的脏数据真正落盘后才返回。这也是数据库配置里 fsync 策略为什么那么敏感的原因——关掉 fsync 性能暴涨但崩溃就可能丢数据。内核回写节奏被几个参数控制cat /proc/sys/vm/dirty_ratio cat /proc/sys/vm/dirty_background_ratio cat /proc/sys/vm/dirty_expire_centisecsdirty_ratio是进程写脏页的硬上限达到后强制同步刷dirty_background_ratio是后台开始刷的阈值dirty_expire_centisecs是脏页最老可以待多久默认 3000也就是 30 秒。生产服务器调优时会动这些值但我建议新手别乱调除非你很清楚自己在做什么。理解了这套机制很多现象就有了解释为什么write很快、为什么拔 U 盘前必须sync、为什么数据库服务器特别怕断电。6.3 断电恢复journal 日志如何救场如果真断电了文件系统靠什么保证不整个崩溃答案是日志journal。ext4 默认dataordered模式先把元数据写进日志区数据块按顺序落盘崩溃后重放日志就能把元数据恢复到一个一致状态。XFS 有自己的 log 区原理类似只是实现更激进日志还支持放到独立设备上。相关查看命令dumpe2fs -h /dev/sdb1 | grep -i journal xfs_info /dev/sdb1journal 不是存档它是“这次操作准备怎么改”的备忘录。有了它断电后的文件系统检查才能从全盘扫描变成局部回放几分钟甚至几秒钟完成。这也就是为什么 ext4 的e2fsck、XFS 的xfs_repair能在大多数掉电后快速收尾。你在京东买的台式机突然断电开机自检时那个 “checking file system” 就是在重放日志。7. 排查实战链路视角下的四个高频存储故障7.1 案例一df 满但 du 查不到大文件经典场景df -h显示/使用率 99%但你在/下du -sh *挨个算加起来明显不到 99%。原因通常是一个已经被删除的文件还被某个进程占用着。删文件只是把目录项和 inode 链接解除。只要进程还握着文件描述符数据块就继续占用磁盘空间直到进程关闭或退出。排查链路df -h lsof L1 | grep deletedlsof L1会列出所有 link count 为 0 但仍被打开的文件看到路径后确认这是哪个进程重启进程或者kill掉空间立刻释放。找到根因前别急着删别的文件先把磁盘山头认清楚。7.2 案例二重启后 /data 消失了一台机器重启后/data不见了通常是 fstab 里设备名写的是/dev/sdb1而这次启动扫描顺序发生变化盘变成了/dev/sdc1。排查顺序先blkid拿到所有分区的 UUID再看 fstab 里设备列是不是还在用设备名改成 UUID执行mount -a验证。如果已经进不了系统用 GRUB 的 rescue.target 应急进单用户模式把 fstab 那行注释掉或者改好再继续启动。这个案例的高频程度远超想象我自己在虚拟机里都被坑过——多块 virtio 盘的序号在变更磁盘配置后会发生重排。用 UUID 之后挂载再也不怕插拔顺序了。7.3 案例三partprobe 报 target is busy新分了区内核不认执行partprobe /dev/sdb报target is busy。含义很简单有进程还在使用分区对应的块设备内核不敢重读分区表。先看看谁占了fuser -v /dev/sdb1fuser -km可以杀掉占用该设备的进程但这条命令要谨慎使用。更稳妥的是先lsof看占用者手动处理后再partprobe或partx -a。还有一种常见情况是分区本身没挂载但某个进程的工作目录就在挂载点目录里它同样会持有设备。这个案例提醒我们对块设备的占用不只有挂载一种形式。7.4 案例四文件系统突然变成只读运行中的 Linux 突然所有写操作报read-only file system常见原因是底层块设备 I/O 错误内核为了防元数据二次破坏主动把文件系统重新挂载为只读。排查链路dmesg | tail -50 df -hTdmesg里通常能看到类似I/O error、blk_update_request failed的记录。这时候别急着mount -o remount,rw。先搞清楚为什么 I/O 会错是磁盘 SMART 报故障、线缆松动、还是 RAID 卡状态异常。盲目 remount 只会让系统在坏盘上继续做高危写入信息更难抢救。我的习惯是发现只读后先把dmesg和 SMART 数据存档再判断是物理替换还是线缆重插硬件恢复后再 remount。我个人干这行的最大体会是存储类故障排查第一个动作永远是确定自己站在链路的哪一层。盘没认到查设备层认到没分区查分区表分区在但挂不上查文件系统和挂载挂上了读写异常再往下钻硬件。另一个值得养的 habits 是任何mkfs、分区、挂载操作动手之前先lsblk和blkid把现状留个截图再操作。这个习惯看起来笨拙但真能在事故复盘时救你一命。链路不难难的是在每一次操作时都清楚自己在链路的哪一环、下一步会影响哪一环。