码上问学问代码之理,学编程之道
教材同步›项目 5
教材同步 · 叶廷东《Linux 操作系统管理(第 2 版)》航空工业出版社 · “十四五”国家规划教材

项目5 Linux 磁盘管理

先建立一张「存储全景图」:一块裸盘怎样一步步变成你能 cd 进去的目录。每个概念都按费曼方式讲——先说人话、再打比方(把硬盘当毛坯仓库、把格式化作装货架做台账)、最后用「删掉这层会怎样」反证分层为什么必须存在。理解了「块设备 → 分区 → 文件系统 → 挂载 → 目录树」这条链,再看 lsblk / fdisk / mkfs / mount 才知道每一步在解决什么问题。随后是配额、软 RAID、LVM 三件进阶武器。操作有风险,务必在快照后练。

① 概念全景 ② 文件系统案例室 ③ 为什么要这样分层 5.1 识别·分区·格式化·挂载 5.2 磁盘配额 5.3 软 RAID 5.4 LVM 5.5 故障排查 拓展训练 教材案例答案册
📙
配套答案册:课件里的 4 个任务案例(加盘挂载 / 磁盘配额 / 软 RAID5 / LVM 扩容)和 3 道课后习题,我已逐条整理成可执行答案 + 每步作用 + 每个参数含义,见 项目5 案例答案册 →。建议先在本页学完流程,再去答案册对答案。
概念篇

先看懂这张图:一块盘是怎么变成目录的

不背命令,先建立整体关系
0.1五层链路:存储的「生产线」

Linux 里没有 C 盘 D 盘的概念。一块新硬盘插上机器,要经过分区 → 格式化(建文件系统)→ 挂载三步,才能被当成「文件夹」访问。下面这张图是本章的总纲,后面所有命令都只是在这条链的某一环上干活。

先别管术语:假设你有一块空地,要把它变成能开张的市场

你手上是一块光秃秃的空地(新硬盘)。现在要让人能进去买菜(读写文件),你会怎么干?

第一步砌隔墙把空地隔成几间:一间卖菜、一间卖肉、一间放垃圾。墙上贴一张平面图,写明「哪面墙到哪面墙是谁的」。
第二步装货架、编台账空房间还是没法做生意。得摆货架、给每个位置编号,再准备一本账本:谁家的货、放在第几排第几格。
第三步挂门牌、接进导览图房间弄好了,但外面的人不知道它在哪。给它挂上「3 号门 · 水果区」的牌子,写进市场总导览图。
第四步顾客只认区域名顾客只说「我去水果区」,不关心它在第几面墙后面、是哪个老板租的。

换成 Linux 的说法,这四步一模一样:

空地 = 块设备(/dev/sdb);砌隔墙贴平面图 = 分区 + 分区表(fdisk);装货架做台账 = 格式化建文件系统(mkfs);挂门牌接进导览图 = 挂载到某个目录(mount);顾客只认区域名 = 统一的目录树(/data)。

所以这条链不是「Linux 故意绕圈子」,而是把一件大事拆成四件各管一段的小事:分区管「切多大」,文件系统管「怎么存、怎么找」,挂载管「怎么接进来」,目录树管「用户怎么记」。拆开以后,任何一段都能单独换掉——这才是后面所有命令存在的理由。

从一块硬盘到一个目录:五层关系 上层依赖下层;每一层都有自己的「命令」 ① 块设备层 物理磁盘 /dev/sdb(也可以是 /dev/vda、/dev/nvme0n1) 裸设备:只能按固定大小的「块」读写,没有文件、没有目录 看它:lsblk / lsblk -f TYPE 列显示为 disk ② 分区层 分区 /dev/sdb1 + 分区表(MBR 或 GPT) 把整块盘切成几段,分区表记录「第几段到第几段」属于谁 划它:fdisk(MBR)/ parted(GPT) TYPE 列显示为 part ③ 文件系统层 文件系统 xfs / ext4(swap 是另一类:交换空间) 格式化 = 在这段空间上「盖房子」:超级块 + inode + 数据块 建它:mkfs.xfs / mkfs.ext4 FSTYPE 列显示为 xfs / ext4 ④ 挂载层 挂载点 /data(一个空目录) 把文件系统「接」到目录树的某个位置;不挂载就访问不到 挂它:mount;开机自动:/etc/fstab MOUNTPOINT 列显示为 /data ⑤ 目录树层 统一的目录树:/ 为主根,/data 挂在它下面 用户和程序只认路径 /data/file.txt,不关心底层是哪块盘 用它:cd /data、df -h 看用量 一条路径,背后可能跨越很多块盘 记牢这条生产线:新盘 → 分区 → 格式化 → 挂载 → 写 fstab,五步缺一不可
图 1 · 存储五层关系:上层的每一步都依赖下层已完成。命令只是某一层的「操作杆」。

但这张图还只是「名字」。比喻必须能落到实体上——否则命令永远靠背。下面把五层逐一换成系统里真实存在的东西:叫什么、在哪、用什么命令能亲眼看见。

层生活比喻Linux 里对应的真实概念(实体)怎么亲眼看到它
① 块设备 毛坯仓库 块设备文件 /dev/sdb(主次设备号 8:16),由内核的块设备驱动提供,在 /sys/block/sdb/ 下有自己的目录树。它只会「按块读写」,没有文件、没有目录、没有权限 lsblk -d(TYPE 列 = disk)
ls -l /dev/sdb(行首是 b)
② 分区 隔墙 + 平面图 分区表(MBR 占第 0 扇区的 512 字节;GPT 用前 34 个扇区且首尾各存一份)+ 分区设备 /dev/sdb1。注意:分区设备并没有把盘「切开」,它只是把「起始 LBA + 扇区数」包成一个可访问的窗口 fdisk -l /dev/sdb(看 Start / Sectors)
lsblk(TYPE 列 = part)
③ 文件系统 货架 + 编号 + 台账 mkfs 写进分区的那一整套元数据:超级块(总账封面)、块位图 / inode 位图(哪格空着)、inode 表(每个文件的编号卡)、数据块(货位)、目录项(名字 → 编号的索引)。ext4 / xfs 只是这套结构的两种不同排版 lsblk -f(FSTYPE 列)
dumpe2fs -h /dev/sdb1(ext4 看超级块)
xfs_info /dev/sdb1(xfs)
④ 挂载 挂门牌 + 接进导览图 内核里的一张 mount 表:一条记录 = 设备 + 挂载点 + 文件系统类型 + 挂载选项。它把某个文件系统的「根」绑定到目录树里的一个 dentry(挂载点),路径解析走到这里就「跳」进那个文件系统 findmnt
cat /proc/self/mountinfo
df -hT
⑤ 目录树 顾客只认区域名 唯一的根 / 与 VFS 的统一命名空间:路径解析(namei)在跨文件系统时透明拼接。所以你写下 /data/report.txt 时,内核可能已经穿过两块盘、一个 LVM、一个 RAID findmnt --target /data(看这条路径落在哪个设备上)
stat -f /data
对不上的典型症状说「分区就是 C 盘」→ 会以为 /data 一定是某个分区;其实 LVM 的逻辑卷、RAID 阵列、甚至网络存储,都能挂成 /data,路径和物理盘从来不是一一对应。

下面六张卡,每个概念都按同一个顺序讲:说人话(不用术语的一句话)→ 打个比方(生活里对应的东西)→ 为什么有这层(如果没有它会出什么事)→ 术语对照(考试和文档里的正式叫法)。

块设备(block device)

说人话在内核眼里,一块硬盘就是一大片能按格子读写的地方:告诉它「读第 1024 号格子」,它就把那 4KB 内容给你。

打个比方一整栋还没隔间的毛坯仓库:门开着、地是平的、能往里搬东西,但没有房间、没有编号、没有台账。

为什么有这层因为程序不该关心「你的盘是三星还是西数、插在哪个口」。内核把它统一成 /dev/sdX 一个文件,读写硬盘就跟读写文件一样;换硬件不用改一行程序。

术语对照:命名 sda/sdb(SATA/SCSI/USB)、vda(虚拟机)、nvme0n1(NVMe 固态);分区在名字后加序号。用 lsblk 看,TYPE 列 = disk。

分区表:MBR 与 GPT

说人话一张贴在磁盘最开头的小纸条,上面写着「第 0~100G 归 A,第 100~200G 归 B」。

打个比方在毛坯仓库里砌隔墙,再在门口贴一张平面图。墙让人各占一块,平面图让后来的人知道哪块是谁的。

为什么有这层因为一块盘要同时装系统和数据,必须划清边界:重装系统只格式化系统那一间,数据那间不受影响;某个目录写爆了,也只撑爆自己那间,不会拖死整块盘。

术语对照:MBR 最多 4 主分区、单盘 ≤2TB;GPT 可 128 分区、容量几乎不限、有备份表。换分区表 = 清空数据。TYPE 列 = part。

文件系统(filesystem)

说人话给这段空间建一套台账:每个文件叫什么、多大、内容放在哪几块,全记下来,于是你能用「名字」找东西。

打个比方在隔好的房间里摆货架、贴编号、做账本。没有货架,货只能堆在地上——想找一件就得翻遍全场。

为什么有这层因为磁盘只会「读第 N 块」,它根本不认识「a.txt」。文件系统负责把「名字 / 目录」翻译成「块号」,顺便管权限、碎片、配额、断电后的修复。没有它,你就得自己记「我的数据在第 58712 块」。

术语对照:xfs / ext4 / vfat;三大件 = 超级块(全局信息)、inode(每个文件的元数据)、数据块(内容)。FSTYPE 列空 = 还没格式化。

挂载与挂载点

说人话把某个准备好文件系统的分区,「接」到目录树的某个位置上,从此走这个目录就等于走进那块盘。

打个比方给仓库挂门牌,并把门牌登记进市场总导览图。没挂门牌的仓库,顾客永远找不到——东西在里面也等于不存在。

为什么有这层因为 Linux 只有一棵目录树,没有盘符这回事。挂载把「物理盘」和「路径」解耦:/home 可以从旧盘搬到新盘,用户的路径一个字都不用改;U 盘插上挂到 /media/xx 就出现了。

术语对照:挂载点必须是已存在目录;目录原有内容会被遮住,卸载后回来(所以挂载点通常选空目录)。开机自动挂载写在 /etc/fstab。

swap 交换空间

说人话内存不够用时,把暂时用不到的内存内容先挪到磁盘上放着,等要用再换回来(代价是慢)。

打个比方书桌放不下的资料先塞进抽屉。桌上永远只留正在看的,抽屉当「临时仓库」。

为什么有这层因为内存一旦耗尽,内核会启动 OOM killer 直接杀掉进程(可能杀掉你的数据库)。有 swap 就能先缓一缓,把不活跃的页面挤出去。

术语对照:swap 不是文件系统,用 mkswap 初始化、swapon 启用;fdisk 里分区类型要改成 82。查看用 swapon --show / free -h。

容量 vs inode

说人话一个文件系统有两份额度:能装多少「斤货」(块容量),和能贴多少「张标签」(inode 个数)。

打个比方货架还空着一大半,但标签用完了——新货照样上不了架,因为没法登记。

为什么有这层因为每个文件都必须占一个 inode(记录元数据)。小文件一多(邮件队列、缓存、日志碎片),容量还剩很多却先报「No space left on device」——这不是 bug,是另一份额度用完了。

术语对照:df -h 看块容量,df -i 看 inode;df -i 接近 100% 时,要么删小文件,要么重建文件系统调大 inode 比例。

这些比喻凭什么成立?把每个「零件」拆到字节级看

比喻不是修辞,而是同构:如果仓库里的某样东西,在磁盘上也能找到干同一件事的结构,这个比喻才算站得住。下面逐层拆开——重点看第 ③ 层,那也是学生最容易「听懂了但没真懂」的一层。

① 块设备 = 毛坯仓库空地能进货,但没有房间、没有编号
  • 仓库有地能进出货 真实:/dev/sdb 是一个块设备文件,open/read/write 就是「往盘上搬东西」。为什么等价:仓库只能整车进出,你没法从中间抠走一件——磁盘也一样,内核只允许整块(512B / 4K)读写,改 1 个字节也要「读整块 → 改 → 写回」。
  • 地上没有货位号只能靠坐标找 真实:块设备没有文件名、没有目录、没有权限,只能按 LBA 逻辑块地址定位。为什么等价:空地上说「第 3 米处」才找得到东西,说「白菜」没人知道在哪——这就是为什么裸盘上取数据必须给块号。
② 分区 = 砌隔墙 + 贴平面图墙是边界,图是记录
  • 砌的那道墙不可越界 真实:分区表项里的一对数字——起始 LBA + 扇区数。写分区表只改第 0 扇区那 512 字节(MBR),一个数据字节都不搬。为什么等价:墙只是划界,不搬货。所以「删掉分区表」数据仍在原地——这正是 testdisk 能恢复分区的原理。
  • 门口那张平面图哪块归谁 真实:MBR = 446B 引导代码 + 4 条 × 16B 分区项 + 2B 标志 55AA;GPT 则在首尾各存一份完整分区项表。为什么等价:图没了墙还在,但没人知道墙里归谁——记录(元数据)比实体更容易丢,所以 GPT 要留备份。
  • 「房间」本身不是切出来的 真实:/dev/sdb1 只是内核按「起始 LBA + 长度」造出来的一个窗口设备。为什么等价:隔墙并没有真的把地搬走一块,只是从此只从这扇门进出。
③ 装货架 + 贴编号 + 做台账 = mkfs这一层最能说明「为什么不是随便打个比方」
  • 货架的格子等大的货位 真实:mkfs 把分区切成固定大小的块(ext4 默认 4096 字节),此后所有分配都以块为单位。为什么等价:货位必须等大才好编号、才好回收——磁盘上「每格 4K」正是为了让「第几块」这个编号有意义。
  • 每件货的编号卡不装货,只写信息 真实:inode。每个文件 / 目录占一个 inode,编号唯一(ls -i 可见),里面记大小、权限、时间戳、链接数,以及「内容在哪几个块」的指针。为什么等价:编号卡上写「这票货 3 箱,箱号 8812/8813/8814」,但货不放在卡上——inode 也不存文件内容,只存「去哪找」。
  • 那本台账按名字查编号 真实:目录项(dentry)。目录本身也是一个文件,它的「内容」就是一张 文件名 → inode 号 的表。为什么等价:查台账 = 用名字换编号。关键推论:inode 里没有文件名,所以 mv 改名只是改台账那一行,数据块一格都没动——这就是改名「瞬间完成」的原因。
  • 账本封面一共多少页 真实:超级块:块大小、总块数、inode 总数、空闲数、magic 编号、挂载状态。为什么等价:封面只写「本账共几页、每页几行、已用几行」,不写具体的货——挂载时内核读的就是它,读不懂(magic 不对)就报 wrong fs type。
  • 盘点表哪格还空着 真实:块位图 / inode 位图,1 bit 对应一格 / 一个 inode。为什么等价:盘点表就是「一格一个勾」。关键推论:mkfs 只写这几张表 + 把位图清零,根本不擦数据区——所以格式化几秒完成,也所以格式化后的旧数据「理论上」还能被恢复。
  • 为什么非有这层翻译的工作 真实:块设备只会「读第 8812 块」,而人只会说「打开 report.txt」。为什么等价:仓库管理员干的就是把「我要那箱苹果」翻译成「B 区 3 排 2 层」——文件系统就是内核里的那个管理员,缺了它,你就得自己背块号。
mkfs 到底在分区里写了什么(ext4 简化布局) 格式化不是「擦掉数据」,而是把下面这几张表写好、把空闲位清零——所以几秒就完成 引导块 1KB 不参与比喻 超级块 总账封面 块大小 / 总数 块位图 哪些格子空着 1 bit / 块 inode 位图 哪些编号卡空着 1 bit / inode inode 表 一排编号卡(131 号在这里) 元数据 + 块指针 数据块区 真正的货位(文件内容) 占整块 90% 以上 省略:GDT 块组描述符、日志区、保留块。xfs 用多个 AG(分配组)实现同样的三件套:超级块 + 空闲空间 B+ 树 + inode B+ 树。 打开 /data/report.txt 时,内核依次拉开三个抽屉 ① 目录项:名字 → 编号 report.txt → inode 131 notes.md → inode 128 目录本身也是个文件,内容是这张表 ② inode 131:编号卡 大小 4096 B · 权限 644 块指针 → 8812 无文件名;名字只在上一步 ③ 数据块 8812:货 Hello, Linux! 4 KB 一格,装的才是内容 所以:文件名只存在于目录项,不在 inode 里 —— mv 改名只是换台账一行,货一格没动 反过来:删文件也只是删掉目录项 + 把 inode 和块位图置空,内容仍躺在原处 → 这才是「误删能恢复」的物理原因
图 2 · mkfs 的产物(上)与一次文件查找的三步(下)。图上每个方块都能对回「货架 / 编号卡 / 台账 / 盘点表」这套比喻。
④ 挂载 = 挂门牌 + 接进导览图门牌不改变仓库,只改变「怎么找到它」
  • 门牌贴在外面 真实:内核 mount 表里的一条记录:设备、挂载点、文件系统类型、挂载选项(可 findmnt 看到)。为什么等价:门牌不属于仓库本体。关键推论:mount 一个字节都不写盘,只改内核内存里的映射——所以重启就没了,必须写 /etc/fstab。
  • 接进导览图路口有人引路 真实:路径解析走到挂载点时,VFS 把该 dentry 指向的目标换成子文件系统的根 inode,然后「跳」进去继续解析。为什么等价:顾客按导览图走,管理员在路口把他引到另一栋楼——路径字符串没变,脚下的盘换了。
  • 原来的东西看不见被遮住 真实:挂载点目录原来的 inode 被替换,原内容仍在盘上,umount 后回来。为什么等价:门牌挂上去,墙上原来的字被盖住了,但墙没拆——所以挂载点要选空目录,否则「文件像丢了」。
⑤ 目录树 = 顾客只认区域名路径是「名字」,不是「位置」
  • 「我去水果区」不需要知道在哪 真实:路径字符串 /data/report.txt,由 VFS 逐级解析。为什么等价:顾客只报区域名,管理员负责换算成位置——用户敲路径,内核负责换算成「哪块盘、哪个 inode、哪个块」。
  • 换老板顾客不知道名字不变 真实:把 /data 从旧盘迁到新盘 / 新 LVM,只需改 mount 表(或 fstab + 重启),程序里所有路径一个字不用改。为什么等价:水果区换了老板,导览图上还是「水果区」三个字。
  • 一个市场,一个导览图但可以有多份副本 真实:VFS 的命名空间是每个进程可见的「视图」,容器(mount namespace)能各自看到不同目录树。为什么等价:同一个市场可以印不同版本的导览图——给游客一份、给内部员工一份。
⚠
比喻在哪里会失效(诚实说明):① 现实的墙能挡人,分区的边界只是「账面上的」,越界写不会报错,只会让文件系统损坏;② 搬货要人动手,磁盘分配块由位图 + 算法在微秒内完成;③ 门牌是行政动作,mount 是内核内存里的映射,而且可以有多个视图(容器);④ 市场只有一个入口,Linux 的 / 可以被 chroot、容器「换一个根」。比喻帮你建立直觉,机制才决定你能不能排错。
0.2文件系统案例室:10 个能当场验证的现象

「文件系统是抽象层」这句话最容易让人似懂非懂。破解办法不是再讲一遍原理,而是给你 10 个能亲手复现的现象——每个现象都只在某个结构被用满、被改写、或者被跳过时才会出现。看完结构图再去敲命令,现象和结构就对上了。

01空目录为什么也占 4 KB?
stat -c 'size=%s blocks=%b' /data/lab

因为目录也是文件。它同样占一个 inode,还至少占一个数据块——这个块里存的就是那张「文件名 → inode 号」的台账。

对应图 2:inode 表 + 数据块(目录自己那一格)
026 个字节的文件,为什么也占 4 KB?
stat -c '%s 字节 / %b×512' /etc/hostname

分配的最小单位是块,不是字节。不足一块也独占一整块(默认 4 KB)。这就是 ls -l 和 du 经常对不上的第一个原因。

对应图 2:块位图 + 数据块
03ls 说 1 GB,du 说 0?
truncate -s 1G f; ls -lh f; du -h f

稀疏文件:inode 里记了「大小 = 1 GB」,但一个块都没分配,位图里对应的位还是空。读它得到全 0,因为内核把「没分配的块」直接当 0 返回。

对应图 2:inode 的大小字段 ↔ 块指针(两者可以不一致)
04两个文件名,同一个 inode 号?
ln a.txt b.txt && ls -li a.txt b.txt

这就是硬链接:台账里多写了一行,指向同一张编号卡。删掉其中一个名字只是链接数 −1,减到 0 才真正回收块——这条现象直接证明「文件名不在 inode 里」。

对应图 2:目录项(台账)多一行,inode 表不动
05mv 秒完成,cp 却要等半天?
time mv /data/a.iso /data/b.iso

同分区改名只改台账:两个目录项换个名字,数据块一格没动。而 cp 要把内容真搬一遍。跨分区 mv 例外——那其实是「复制 + 删除」,一样慢。

对应图 2:目录项(台账)
06还剩 30 G,却写不进去?
df -h /data; df -i /data

写一个文件要同时拿两份额度:1 个 inode + 若干块。小文件堆积(缓存、邮件、日志碎片)先把 inode 用光,于是容量空着也报 No space left on device。

对应图 2:inode 位图(块位图还有空位)
07rm 掉 10 G,df 一点没变?
lsof +L1 | head

还有进程开着这个文件。文件被删的只是名字,inode 引用数不为 0,块就不释放。重跑服务(或清空那个 fd)后空间才回来——这也是「删了日志、磁盘不降」的元凶。

对应图 2:块位图(位还没被清零)
08格式化之后,数据真的没了?
dd if=/dev/sdb1 skip=100 bs=1M count=1 | strings

没有。mkfs 只重写左侧那几格元数据,数据区一个字节没动,写进去的特征串原地还在(完整复现见下方实验 B)。所以格式化 ≠ 销毁;反过来卖旧盘前必须用 dd if=/dev/urandom 覆写。

对应图 2:条带左侧几格被重写,右侧数据块区原样
09突然断电,文件系统凭什么不坏?
dumpe2fs -h /dev/sdb1 | grep -i features

靠日志(journal / log)。改数据前先写一条「我打算改什么」到日志区,改完才标记完成。掉电后挂载时重放日志,把做了一半的操作补完或撤销。

对应图 2:未画出的「日志区」(ext4 journal / xfs log)
10xfs 能扩不能缩,是真的?
xfs_growfs /data  (xfs 只有扩,没有缩)

真的。xfs 只提供在线扩容,要缩小只能备份、重建、再灌回去;ext4 可以 resize2fs 缩。选型口诀:以后可能要缩 → ext4;只扩不缩 → xfs。

对应图 2:同一套结构(超级块/位图/inode)的两种排版差异

其中 04 和 08 建议现场敲一遍——它们最能说明「货架 / 编号卡 / 台账」这套比喻不是随口打的比方。

bash · 实验 A:用硬链接证明「文件名不在 inode 里」
mkdir -p /data/lab && cd /data/lab
echo "hello" > a.txt
ln a.txt b.txt                # 建硬链接:台账里加一行,指向同一张编号卡
ls -li                        # -i 显示 inode 号,-l 看链接数
# 131081 -rw-r--r-- 2 root root 6 Mar 12 10:00 a.txt
# 131081 -rw-r--r-- 2 root root 6 Mar 12 10:00 b.txt
#  ↑ 同一个 inode 号            ↑ 链接数都是 2

rm a.txt                      # 只删掉「a.txt」这一行台账
ls -li
# 131081 -rw-r--r-- 1 root root 6 Mar 12 10:00 b.txt
#  ↑ inode 没变,链接数变成 1,内容一个字没少

stat -c 'inode=%i 链接=%h 大小=%s' b.txt
# inode=131081 链接=1 大小=6
结论:名字是台账上的一行,不是货上的标签。所以改名瞬间完成、删除只减计数——理解这一点,mv / rm / ln 的行为就全解释通了。
bash · 实验 B:证明 mkfs 不擦数据区(虚拟机/测试盘做,勿用系统盘)
# ① 往测试分区第 100 MB 处写一个特征串
echo "WENXUE-MAGIC-2026" | dd of=/dev/sdb1 bs=1M seek=100 conv=fsync
# ② 格式化之前,先在原位置找到它
dd if=/dev/sdb1 bs=1M skip=100 count=1 2>/dev/null | strings | grep WENXUE
# WENXUE-MAGIC-2026            ← 数据在这里(偏移 100 MB 处)

mkfs.ext4 -F /dev/sdb1         # 重新格式化

# ③ 格式化之后,再找一次
dd if=/dev/sdb1 bs=1M skip=100 count=1 2>/dev/null | strings | grep WENXUE
# WENXUE-MAGIC-2026            ← 它还在原地!
结论:mkfs 只重写了超级块、位图、inode 表这几张「表」,数据区一格没动。所以「格式化能恢复数据」在物理上成立;也所以处理旧硬盘必须覆写,而不是格式化一遍就卖掉。
⚠
案例 07 / 08 的两个前提:①「删了不释放」只在进程仍持有句柄时成立,重启进程后空间自然回来;②案例 08 的「能恢复」是物理条件成立,实际能否救回取决于写入量——只要系统继续写盘,旧块就会被覆盖,所以误格式化后第一件事是立刻停写。
0.3两种「改造」:RAID 与 LVM 插在哪一步

普通分区是「一块盘切几段」。如果想让多块盘更安全(坏一块不丢数据)或更弹性(空间不够随时扩),就要在「分区」和「文件系统」之间插一层:软 RAID 或 LVM。它们最终都表现为一个块设备(/dev/md0 或 /dev/vg0/lv0),后面的格式化、挂载完全一样。

RAID 与 LVM:都是在「分区」与「文件系统」之间加一层 软 RAID:多块盘 → 一个整体(要安全) /dev/sdb /dev/sdc RAID 1 镜像 /dev/md0 两份完全相同 mkfs 挂载 · 一块盘坏了,数据还在(RAID 1 / 5 / 10) · 命令:mdadm;查看:/proc/mdstat · 代价:空间利用率下降(镜像只剩一半) · 典型场景:数据库、重要文件服务器 适合「怕坏」:宁可少一半容量,也要数据安全 LVM:盘 → 池 → 逻辑卷(要弹性) /dev/sdb /dev/sdc PV 物理卷 pvcreate 标记为可用 VG 卷组(池) vgcreate / vgextend 多块盘合成大池子 LV0 10G → /data LV1 5G → /logs · 空间不够:加盘 → vgextend → lvextend,无需重分区 · 一个池子可切成多个逻辑卷,各自挂不同目录 · 注意:扩完 LV 还要扩文件系统,df 才看得见
图 3 · 软 RAID 与 LVM 的区别:RAID 用「冗余」换安全,LVM 用「抽象」换弹性;两者都可以叠加使用(先做 RAID,再做 LVM)。

这两个东西为什么不叫「命令」而叫「一层」?

软 RAID = 复印机。说人话:把好几块盘当成一块用,同一份数据同时写进多块盘(或者分摊写、另存校验),坏掉一块还能把内容算回来。打个比方:重要合同复印两份、放两个抽屉,丢一个抽屉不心疼。为什么要有它:单块盘什么时候坏是概率问题,硬盘坏是常态而不是意外;用便宜的盘堆出「不宕机」,比买一块贵盘便宜得多。

LVM = 水库。说人话:先把几块盘倒进一个大池子,再从池子里按需「切」出任意大小的卷。打个比方:散装水库随时按瓶装水灌,比分区分装灵活。为什么要有它:分区是「一锤子买卖」——划多大就多大,想改要卸载、甚至重装;LVM 把「容量怎么分」这件事从「装机时」推迟到「用的时候」,不够了加盘扩池再切,业务不停。

那它们为什么正好插在「分区」和「文件系统」之间?

因为文件系统只认一件事:给我一块能读写的块设备。RAID 把自己伪装成 /dev/md0,LVM 把自己伪装成 /dev/vg0/lv0——在上面看,它们和 /dev/sdb1 长得一样。所以你能照常 mkfs、照常 mount,一行代码都不用改。这就是分层的真正价值:只要接口不变,中间随便加东西。

0.4为什么要这样分层:把每一层「删掉」试试看

讲「分层好」最没用的说法就是「这样比较清晰」。真正让人信服的问法是:如果拿掉这一层,会发生什么?下面这张图就干这件事——假设你真的「删掉」某一层,看看代价是什么。

反证法:把任意一层删掉,会出什么事 每一层都在替上一层挡掉一种复杂度;删掉它,复杂度就漏给写程序的人和用电脑的人 删掉块设备层 /dev/sdb 程序里直接写死「读 sdb 第 1024~1031 号扇区」 ✗ 换一块型号不同的盘,读写位置全变,代码推倒重写 删掉分区层 分成几间 整块盘只做一个文件系统,系统和数据混在一起 ✗ 重装系统 = 数据一起被格式化;也没法装双系统、没法留 swap 删掉文件系统层 mkfs 只能按块号读写裸盘,自己维护「哪个文件在第几块」 ✗ 没有文件名、没有目录、不知道文件多大——回到 1950 年代 删掉挂载层 mount 每块盘在装机时就把路径绑死,想换位置要改所有程序配置 ✗ U 盘插上永远不会出现;/home 想换块盘等于全盘改路径 删掉目录树层 一棵 / 树 每个程序自己记住「我的数据在 sdb1 上」 ✗ 用户得记物理位置;多个程序互相踩盘,谁也管不了谁 分层的本质:每层只暴露一个简单接口,把下层的复杂藏起来——你只跟「最近的那层」打交道
图 4 · 五层不是「规定」,而是五次踩坑之后固化下来的分工。哪一层被抽掉,它挡住的麻烦立刻回到你身上。

顺着这个思路,就能回答「为什么要这样分层」的三个真实理由:

① 可替换:换一层不动别层

ext4 换成 xfs 不用重新分区;机械盘换成固态盘不用改程序;甚至把底下的盘换成 RAID,上面的文件系统毫不知情。因为接口没变。

② 可组合:像插积木一样叠

盘 → RAID 1 → LVM → xfs → /data,五层叠起来照样工作。这是分层最实用的红利:没人需要为你发明一个「RAID+LVM 专用文件系统」。

③ 可隔离:坏一块不塌全局

/var 日志写爆不会拖死 /;某个学生配额超了不影响别人;一个分区误操作,别的分区还在。边界本身就是保护。

费曼自检:讲不出来,就是还没真懂

费曼技巧的第三步不是「再看一遍」,而是合上讲义,试着讲给别人听。卡住的地方,就是你理解的漏洞。下面三题,能顺畅讲完就说明这一章的地基打好了:

  1. 用一句人话说清:为什么 mkfs 做完了、mount 之前,还是访问不了这块盘?(提示:门牌还没挂)
  2. /data 报「空间不足」,你能说出删文件 / 加块盘 / 扩逻辑卷这三种做法,各自动的是哪一层吗?
  3. 老板问「为什么不直接把盘分成 C、D 两个区,像 Windows 那样?」——你会怎么用「删掉某一层会怎样」来回答?
0.5场景对照:我要做什么 → 用哪条命令

学命令最怕「不知道什么时候用」。先记住这张对照表,后面每条命令的细节再按需展开。

我要解决的问题第一步看动手命令对应小节
机器上有几块盘?分区和文件系统是什么?lsblk -fblkid 看 UUID5.1.1
哪个目录快满了?还剩多少空间?df -hdu -sh /* 定位大户5.1.1
新加了一块盘,要切成一个分区lsblkfdisk / parted5.1.2
分区建好了,但还是用不了——mkfs.xfs 格式化5.1.3
格式化好了,怎么访问它?——mount + 写 /etc/fstab5.1.4
限制某个学生只能用 200Mxfs_quota reportxfs_quota limit5.2
一块盘坏了不能丢数据cat /proc/mdstatmdadm --create5.3
空间不够,想在线扩容vgs / lvsvgextend + lvextend5.4
一句话主线看(lsblk/df/du)→ 分(fdisk/parted)→ 格式化(mkfs)→ 挂载(mount/fstab)→ 进阶(quota/RAID/LVM)。永远先「看」,再动手。
0.6安全须知(动手前必读)
⚠
这些操作会清空数据,且不可撤销:fdisk 里按 w 保存、mkfs 格式化、mdadm --create 建阵列、pvcreate 初始化 PV。练习前先给虚拟机打快照;生产环境操作前先备份,并再三核对设备名(/dev/sdb 和 /dev/sda 一字之差,可能就是系统盘)。
  • 先在虚拟机里练,不要拿真机系统盘练手;确认设备名用 lsblk 而不是凭印象。
  • 看到 w、mkfs、--create 这类「写入型」操作,先停下来读一遍提示。
  • 改 /etc/fstab 后,先用 mount -a 验证,没问题再重启。
任务 5.1

从识别到可用:查看、分区、格式化、挂载

一条完整生产线走通
5.1.1先学会「看」:lsblk / blkid / df / du

动手之前必须知道现状。四个命令分工不同:lsblk 看硬件与分区结构,blkid 看文件系统类型与 UUID,df 看已挂载文件系统的用量,du 看目录里谁在占空间。

lsblk★ 必会

列出所有块设备,以树状展示「磁盘 → 分区 → LVM/RAID」的从属关系。它读的是内核设备树,不需要挂载也能看。

参数详解
常用选项
  • lsblk 默认树状列表:名称、大小、类型、挂载点
  • -f 增加 FSTYPE、LABEL、UUID(最常用)
  • -p 显示完整设备路径(/dev/sdb1 而非 sdb1)
  • -d 只显示整盘,不显示分区
  • -o NAME,SIZE,TYPE,MOUNTPOINT 自选列
  • -J 输出 JSON,便于脚本处理
输出里要认的列
  • NAME 设备名;SIZE 容量;TYPE 类型
  • TYPE=disk 整块盘;part 分区;lvm 逻辑卷;raid1 阵列
  • FSTYPE 文件系统(xfs/ext4/swap);空 = 还没格式化
  • MOUNTPOINT 挂到哪;空 = 未挂载
  • UUID 唯一标识,写 fstab 用它最稳
bash · 看懂一台机器的存储结构
lsblk
# NAME   MAJ:MIN RM  SIZE RO TYPE MOUNTPOINT
# vda      252:0    0   40G  0 disk              ← 第一块虚拟盘,40G
# ├─vda1   252:1    0    1G  0 part /boot        ← 分区1,挂到 /boot
# ├─vda2   252:2    0   39G  0 part              ← 分区2,没直接挂载
# │ └─rl-root 253:0 0   37G  0 lvm  /            ← 它上面做了 LVM,根目录在这
# └─vda3   252:3    0    2G  0 part [SWAP]       ← swap 分区
# sdb      8:16     0   10G  0 disk              ← 新加的盘:没有分区
# └─sdb1   8:17     0   10G  0 part /data        ← 已分好并挂载

lsblk -f
# NAME   FSTYPE      LABEL UUID                                 MOUNTPOINT
# vda1   xfs               a1b2c3d4-...                         /boot
# vda2   LVM2_member       e5f6a7b8-...
# sdb1   xfs         data  7c8d9e0f-...                         /data
技巧:记不住列含义就看 lsblk -f;设备没分区时树里只有一层,分区后会缩进一级。
blkid☆ 常用

查看指定块设备的文件系统类型、UUID、LABEL。写 /etc/fstab 前用它拿 UUID——比 /dev/sdb1 稳定,插拔硬盘后设备名可能变,UUID 不变。

用法
命令
  • blkid 列出所有已格式化设备
  • blkid /dev/sdb1 只看某分区
  • blkid -s UUID -o value /dev/sdb1 只输出 UUID 值,便于复制
对比
  • lsblk -f 看结构 + 文件系统(推荐日常)
  • blkid 看细节属性(UUID/LABEL)
bash
blkid /dev/sdb1
# /dev/sdb1: LABEL="data" UUID="7c8d9e0f-1234-..." TYPE="xfs"

# 只取 UUID,方便直接粘进 fstab
blkid -s UUID -o value /dev/sdb1
# 7c8d9e0f-1234-...
记住:fstab 里写 UUID=xxxx 比写 /dev/sdb1 更可靠。
df★ 必会

查看已挂载文件系统的总容量、已用、可用、使用率。它统计的是文件系统级别的总量,不是某个目录。

参数详解
常用选项
  • df -h 人类可读单位(G/M)
  • df -T 多显示文件系统类型
  • df -i 看 inode 使用情况(文件数)
  • df -h /data 只看某个挂载点
输出列
  • Filesystem 设备或卷名
  • Size / Used / Avail 总/已用/可用
  • Use% 使用率,≥90% 要警惕
  • Mounted on 挂载点
bash
df -hT
# Filesystem     Type  Size  Used Avail Use% Mounted on
# /dev/vda2      xfs    37G  9.8G   27G  27% /
# /dev/sdb1      xfs    10G  1.2G  8.8G  12% /data

df -i
# Filesystem      Inodes  IUsed   IFree IUse% Mounted on
# /dev/sdb1      6553600 120000 6433600    2% /data   ← inode 也要看
判断「满没满」要同时看两行:df -h 看容量,df -i 看 inode 数量。
du★ 必会

逐个目录累加统计实际占用,用来找出「谁把空间吃掉了」。和 df 的区别:df 看总量(问文件系统),du 看明细(爬目录树)。

参数详解
常用选项
  • du -sh /data 汇总该目录总大小
  • du -h --max-depth=1 /data 只看第一层子目录
  • du -ah /data | sort -rh | head 找最大的文件
  • -x 不跨文件系统(避免统计到别处)
经典坑
  • 文件被删了但进程还开着 → df 不降、du 找不到
  • 用 lsof +L1 找「已删除但仍被占用」的文件
  • 重启对应服务后空间才真正释放
bash
du -sh /var/log            # /var/log 总共占多少
du -h --max-depth=1 /data  # /data 下每个子目录各占多少
du -ah /data | sort -rh | head -5   # 最大的 5 个文件/目录
排查顺序:df -h 发现某个挂载点满了 → du --max-depth=1 逐层下钻 → 找到大文件。
5.1.2分区:fdisk(MBR)与 parted(GPT)

分区 = 在盘上划线,告诉系统「这一段归谁用」。划线前先选分区表:老设备用 MBR,新设备与大容量盘用 GPT。

对比项MBR(msdos)GPT
最大分区数4 个主分区(或 3 主 + 扩展分区再分逻辑分区)默认 128 个
单盘最大容量2 TB极大(9.4 ZB 级别)
分区表备份无,损坏难恢复有备份表 + CRC 校验,更抗损坏
启动方式传统 BIOSUEFI(现代机器)
常用工具fdiskparted / gdisk
fdisk★ 必会

交互式分区工具,默认 MBR。进入后是一问一答的菜单,最后按 w 才真正写入磁盘——在此之前都可以 q 退出放弃。

fdisk 交互按键(背下来)
看与改
  • fdisk -l /dev/sdb 不进交互,直接列出
  • n 新建分区(选主/扩展、分区号、起止扇区或大小)
  • d 删除分区
  • p 打印当前分区表(只是预览)
  • t 改分区类型:82=swap,83=Linux,8e=LVM
  • w 写入并退出(此刻才生效!)
  • q 不保存退出
操作流程(新盘 /dev/sdb)
  • ① fdisk /dev/sdb
  • ② n → p → 分区号回车 → 起始回车 → 大小 +10G
  • ③ p 检查无误
  • ④ w 保存
  • ⑤ partprobe /dev/sdb 让内核重读(有时需要)
bash · fdisk 完整分区过程
fdisk /dev/sdb
# 欢迎使用 fdisk...
# 命令(输入 m 获取帮助):n        ← 新建
# 分区类型: p(主) e(扩展)        p   (MBR 最多 4 个主分区)
# 分区号 (1-4, 默认 1):            1
# 第一个扇区 (默认 2048):          回车
# 最后一个扇区, +sectors 或 +size:  +10G   (分 10G)
# 创建了一个新分区 1,类型为 Linux,大小 10 GiB
# 命令:p                         ← 先预览
# 命令:w                         ← 确认写入(不可撤销)
# The partition table has been altered.

lsblk /dev/sdb
# sdb   10G disk
# └─sdb1 10G part               ← 分区已出现,但还没有文件系统(FSTYPE 空)
此时 lsblk -f 的 FSTYPE 为空:分区只是「划好了线」,还不能存文件,下一步要格式化。
parted☆ 大容量盘推荐

支持 GPT,可一条命令完成、也可交互。比 fdisk 更适合 2TB 以上磁盘和脚本化操作。注意:mklabel 会重建分区表,原分区全没了。

关键子命令
建分区表与分区
  • parted /dev/sdb 进入交互
  • mklabel gpt 建 GPT 分区表(清空原有分区)
  • mkpart primary xfs 1MiB 10GiB 建 1M~10G 的分区
  • print 查看;quit 退出
非交互(一条命令)
  • parted /dev/sdb mklabel gpt
  • parted /dev/sdb mkpart primary xfs 1MiB 10GiB
  • parted /dev/sdb print
  • 脚本里用 parted -s 关闭交互提示
bash · 用 parted 给 2TB 新盘建 GPT 分区
parted /dev/sdb mklabel gpt
# 警告:会破坏现有分区表 —— 新盘可以,旧盘务必确认
parted /dev/sdb mkpart primary xfs 1MiB 500GiB
# 从 1MiB 开始(留出对齐空间),到 500GiB
parted /dev/sdb print
# Number  Start   End    Size   File system  Name     Flags
#  1      1049kB  537GB  500GB               primary
parted 修改立即生效,没有 fdisk 的「w 确认」缓冲;操作前一定核对设备名。
⚠
分区后看不到 /dev/sdb1?内核可能还没重读分区表。执行 partprobe /dev/sdb;仍不行就 partx -u /dev/sdb,或卸载后重新扫描。
5.1.3格式化:mkfs 建文件系统

格式化 = 在分区上建文件系统,相当于给空地盖房子:划分超级块、inode 区、数据区。没格式化的分区,mount 会报「wrong fs type」。

mkfs.xfs / mkfs.ext4★ 必会

按文件系统类型格式化。mkfs 是总入口,mkfs -t xfs 与 mkfs.xfs 等价。格式化会清空该分区全部数据。

参数详解
常用写法
  • mkfs.xfs /dev/sdb1 格式化为 xfs(RHEL 默认)
  • mkfs.ext4 /dev/sdb1 格式化为 ext4
  • mkfs.xfs -L data /dev/sdb1 加卷标 data,方便识别
  • mkfs.xfs -f /dev/sdb1 强制覆盖已有文件系统
  • mkfs.xfs -b size=4096 指定块大小(一般默认即可)
查看与验证
  • xfs_info /dev/sdb1 看 xfs 信息
  • tune2fs -l /dev/sdb1 看 ext4 信息
  • blkid /dev/sdb1 确认 TYPE 变了
对比项xfsext4
默认发行版RHEL / CentOS / Rocky 8+、AnolisUbuntu / Debian 常见
大文件性能强(并发、大文件友好)好
缩容不支持缩容(只能扩)支持缩容(需先卸载、检查)
扩容命令xfs_growfs /挂载点resize2fs /dev/xxx
适用场景服务器、数据库、生产默认通用、需要缩容的实验环境
bash · 格式化为 xfs 并验证
mkfs.xfs -L data /dev/sdb1
# meta-data=/dev/sdb1  isize=512  agcount=4 ...
# data     =           bsize=4096  blocks=2621440 ...
# 出现 "data" 卷标与 blocks 信息 = 成功

blkid /dev/sdb1
# /dev/sdb1: LABEL="data" UUID="7c8d9e0f-..." TYPE="xfs"   ← TYPE 从空变成 xfs
注意格式化对象是「分区」/dev/sdb1,不是整盘 /dev/sdb;格式化了整盘,分区就白划了。
mkswap / swapon了解

把分区做成交换空间并启用。swap 不是文件系统,不挂载到目录,而是被内核当「虚拟内存」使用。

三步启用 swap
命令
  • fdisk /dev/sdb → t → 82 改类型
  • mkswap /dev/sdb2 初始化交换分区
  • swapon /dev/sdb2 启用
  • swapon --show / free -h 验证
开机生效
  • fstab 加一行:UUID=xxx none swap defaults 0 0
  • 关闭:swapoff /dev/sdb2
bash
mkswap /dev/sdb2
# Setting up swapspace version 1, size = 2 GiB
swapon /dev/sdb2
free -h
#               total        used        free      shared  buff/cache   available
# Swap:          4.0G          0B        4.0G     ← 增加了 2G
swap 大小经验值:内存 4G 以下给 4G,4~16G 给 4~8G,更大可给 8G 或不给(服务器可关闭)。
5.1.4挂载:mount 与 /etc/fstab

格式化完的分区仍然「没门牌」。挂载就是把文件系统接到目录树上的某个目录,之后访问这个目录就等于访问那块盘。

mount / umount★ 必会

临时挂载 / 卸载。重启后失效——要永久生效必须写 /etc/fstab。

参数详解
mount 常用
  • mount /dev/sdb1 /data 挂载到 /data
  • mount -t xfs /dev/sdb1 /data 显式指定类型(一般自动识别)
  • mount -o ro /dev/sdb1 /data 只读挂载
  • mount -o remount,rw /data 重新挂载并改为可写
  • mount -a 按 fstab 挂载所有(改完 fstab 先跑它验证)
  • findmnt /data 查看某挂载点详情
umount 与占用
  • umount /data 或 umount /dev/sdb1
  • 报 target is busy:fuser -vm /data 看谁在用
  • umount -l /data 延迟卸载(不推荐,治标)
  • 先 cd / 退出该目录再卸载
bash · 临时挂载并验证
mkdir -p /data            # 挂载点必须已存在
mount /dev/sdb1 /data     # 挂载
df -h /data
# Filesystem      Size  Used Avail Use% Mounted on
# /dev/sdb1        10G     0   10G   0% /data   ← 已生效

umount /data              # 卸载(卸载后目录恢复为空)
挂载点里有原文件时会被「盖住」,卸载后才露出来——这就是要用空目录做挂载点的原因。
/etc/fstab★ 必会

开机自动挂载的配置文件,每行 6 个字段。写错可能导致系统启动失败,改完务必 mount -a 验证。

六个字段(从左到右)
字段含义
  • ① 设备:推荐 UUID=xxxx,不推荐 /dev/sdb1
  • ② 挂载点:必须是已存在的目录
  • ③ 文件系统类型:xfs/ext4/swap
  • ④ 挂载选项:defaults;加固可 defaults,nofail
  • ⑤ dump 备份标志:一般 0
  • ⑥ fsck 检查顺序:根分区 1,其它 2 或 0
推荐做法
  • 先 blkid -s UUID -o value /dev/sdb1 拿 UUID
  • 写 UUID=... /data xfs defaults,nofail 0 0
  • mount -a 无报错 = 配置正确
  • 再 reboot 验证真能自动挂载
bash · 永久挂载三步
# ① 取 UUID
blkid -s UUID -o value /dev/sdb1
# 7c8d9e0f-1234-5678-90ab-cdef12345678

# ② 追加到 fstab(>> 是追加,> 会覆盖整个文件,千万别写错)
echo 'UUID=7c8d9e0f-1234-5678-90ab-cdef12345678 /data xfs defaults,nofail 0 0' >> /etc/fstab

# ③ 先验证语法,再重启
mount -a
findmnt /data
# TARGET SOURCE    FSTYPE OPTIONS
# /data  /dev/sdb1 xfs    rw,relatime,...   ← 成功
nofail:设备不存在时也允许开机,避免拔了数据盘就进不了系统;服务器上很实用。
⚠
fstab 写错导致开不了机怎么办?在启动界面按 e 编辑内核参数,在 ro 后加 rd.break(或进救援模式),mount -o remount,rw / 后注释掉错误行,再重启。所以强调:改完 fstab 一定先 mount -a。
任务 5.2

磁盘配额:限制用户能用多少空间

多人共用一台机器时必备
5.2.1配额在管什么:block 与 inode

一台服务器给全班用,如果不管,一个人就能把盘塞满。配额(quota)限制某个用户/组在一个文件系统上能用的「容量(block)」和「文件数(inode)」。

概念含义对应的限制参数
软限(soft)可以短暂超过,超过后进入「宽限期」,到期必须降下来bsoft(容量)、isoft(文件数)
硬限(hard)绝对上限,一旦达到就写不进去bhard、ihard
宽限期(grace)软限到期前的缓冲时间,默认 7 天xfs_quota ... timer
block 配额按容量算(单位 K/M/G)bsoft/bhard
inode 配额按文件个数算(防止海量小文件)isoft/ihard

为什么两种都要限:只限容量,用户可以建几百万个空文件把 inode 耗尽;只限文件数,用户可以写一个超大文件把容量占满。生产上通常两个一起设。

5.2.2xfs 配额完整流程(RHEL 8+ 默认)
xfs_quota☆ 理解

xfs 文件系统的配额管理工具。前提:挂载时必须带配额选项,否则命令会报「Quota not enabled」。

开启 / 设置 / 查看
① 开启(挂载选项)
  • fstab 选项改为 defaults,uquota(用户配额)
  • 组配额用 gquota,目录配额用 prjquota
  • 临时生效:mount -o remount,uquota /data
② 设置与查看
  • xfs_quota -x -c 'limit bsoft=100m bhard=120m isoft=800 ihard=1000 stu01' /data
  • xfs_quota -x -c 'report -h' /data 看全部用户
  • xfs_quota -x -c 'quota -u stu01' /data 看单人
  • xfs_quota -x -c 'timer -u 3d stu01' /data 改宽限期
  • xfs_quota -x -c 'off -u' /data 临时关闭
bash · 给 stu01 设 100M/120M 容量 + 800/1000 文件数
# ① 确认挂载选项里有 uquota
findmnt -o TARGET,OPTIONS /data
# /data /dev/sdb1 xfs rw,relatime,attr2,inode64,uquota   ← 有 uquota 才可继续

# ② 设置配额:容量软限 100M、硬限 120M;文件数软限 800、硬限 1000
xfs_quota -x -c 'limit bsoft=100m bhard=120m isoft=800 ihard=1000 stu01' /data

# ③ 查看
xfs_quota -x -c 'report -h' /data
# User quota on /data (/dev/sdb1)
#                         Blocks                            Inodes
# User      ID      Used   Soft   Hard  Warn/Grace     Used   Soft   Hard  Warn
# stu01   1001        0   100M   120M     00:00         0    800   1000  00:00
Used 为 0 表示还没写东西;Warn/Grace 显示 00:00 表示未进入宽限期。
bash · 验证配额真的生效
# 切到 stu01,往 /data 写 130M(超过硬限 120M)
su - stu01
dd if=/dev/zero of=/data/big.bin bs=1M count=130
# dd: error writing '/data/big.bin': Disk quota exceeded   ← 硬限拦住了
ls -lh /data/big.bin
# -rw-r--r-- 1 stu01 stu01 120M ...                         ← 只能写到 120M
dd 是「造测试数据」的常用工具:bs=1M count=130 表示写 130 个 1MB 块。
⚠
ext4 怎么办?ext4 用另一套命令:quotacheck -cum /data → quotaon /data → edquota -u stu01(交互编辑)或 setquota -u stu01 102400 122880 800 1000 /data。原理相同,工具不同。
任务 5.3

软 RAID:用冗余换安全

一块盘坏了,服务不停
5.3.1先选级别:RAID 0/1/5/6/10 怎么选
级别最少盘数容错能力空间利用率读写特点典型场景
RAID 0 条带2无(坏一块全丢)100%读写都快临时数据、缓存(不怕丢)
RAID 1 镜像2坏 1 块50%读快、写稍慢系统盘、小容量高可靠
RAID 5 校验3坏 1 块(n-1)/n读快、写有校验开销文件服务器、Web 存储
RAID 6 双校验4坏 2 块(n-2)/n写更慢大容量归档、重要数据
RAID 10 镜像+条带4每组坏 1 块50%读写都快数据库等高 IO 场景

一句话选型:要安全选 1 或 10,要容量与性能折中选 5,要极致可靠选 6。RAID 不是备份——它防的是「硬盘坏」,不防误删、病毒、火灾,重要数据仍要另做备份。

5.3.2mdadm 建阵列、查状态、做故障演练
mdadm☆ 理解

Linux 软 RAID 管理工具(md = multiple devices)。建好后是一个新的块设备 /dev/md0,后续格式化、挂载与普通分区完全一样。

建 / 查 / 修 / 停
建与查
  • mdadm --create /dev/md0 --level=1 --raid-devices=2 /dev/sdb /dev/sdc 建 RAID 1
  • --spare-devices=1 /dev/sdd 顺带指定热备盘
  • cat /proc/mdstat 看同步进度(最快)
  • mdadm -D /dev/md0 看阵列详情
故障与持久化
  • mdadm --manage /dev/md0 --fail /dev/sdc 模拟坏盘
  • --remove 移除坏盘;--add 加新盘重建
  • mdadm --detail --scan >> /etc/mdadm.conf 记录配置
  • mdadm --stop /dev/md0 停止阵列
bash · 用两块盘建 RAID 1 并挂载
# ① 建阵列(两块盘做镜像)
mdadm --create /dev/md0 --level=1 --raid-devices=2 /dev/sdb /dev/sdc
# mdadm: array /dev/md0 started.   ← 开始在后台同步

# ② 看同步进度(RAID 1 初次同步可能几分钟)
cat /proc/mdstat
# md0 : active raid1 sdc[1] sdb[0]
#       10483712 blocks super 1.2 [2/2] [UU]     ← [UU] 两块都在线
#       [=>...................]  resync = 5.2%

# ③ 格式化为 xfs 并挂载(和普通分区一样)
mkfs.xfs /dev/md0
mkdir -p /mnt/raid
mount /dev/md0 /mnt/raid
df -h /mnt/raid
[UU] 表示两块盘都正常;出现 [_U] 说明一块盘掉了,需要更换并重建。
bash · 故障演练:拔掉一块盘会怎样
# 模拟 /dev/sdc 故障
mdadm --manage /dev/md0 --fail /dev/sdc
cat /proc/mdstat
# md0 : active raid1 sdc[1](F) sdb[0]
#       [2/1] [_U]        ← F = Failed,但阵列仍在跑,数据可读

# 移除坏盘,换上新盘 /dev/sdd 并重建
mdadm --manage /dev/md0 --remove /dev/sdc
mdadm --manage /dev/md0 --add /dev/sdd
cat /proc/mdstat          # 会看到 recovery 进度,回到 [UU] 即修复完成
这就是 RAID 的价值:坏盘期间业务不中断,换盘后自动同步回去。但同步期间性能会下降。
bash · 开机自动组装 + 永久挂载
# ① 记录阵列配置,重启后才能自动组装
mdadm --detail --scan >> /etc/mdadm.conf

# ② fstab 加挂载(用 md0 的 UUID)
blkid -s UUID -o value /dev/md0
echo 'UUID=xxxx-xxxx /mnt/raid xfs defaults,nofail 0 0' >> /etc/fstab
mount -a
CentOS/RHEL 还需重建 initramfs:dracut -f;Debian/Ubuntu 用 update-initramfs -u。
任务 5.4

LVM:让空间弹性伸缩

不用重分区就能扩容
5.4.1三层结构:PV → VG → LV

LVM 把「磁盘」和「分区」解耦:先把盘登记成 PV(物理卷),再把多个 PV 汇成 VG(卷组,一个空间池),最后从池子里切出 LV(逻辑卷)当分区用。池子里还有空间,就能随时给 LV 扩;池子不够,就往 VG 里加盘。

PV 物理卷

用 pvcreate 把整盘或分区「登记」为 PV,会在设备头部写入 LVM 元数据。登记会破坏原有数据。

VG 卷组

多个 PV 组成的大池子,用 vgcreate 创建、vgextend 加盘。空间以 PE(物理扩展块,默认 4MiB)为单位管理。

LV 逻辑卷

从 VG 切出来的一块,用 lvcreate 创建,设备路径是 /dev/vg名/lv名。对上层来说它就是一个分区。

5.4.2从零建一套 LVM 并挂载
pvcreate / vgcreate / lvcreate☆ 理解

命令链是一条直线:PV → VG → LV → mkfs → mount。中途可用 pvs/vgs/lvs 随时查看三层状态。

建与查
建
  • pvcreate /dev/sdb /dev/sdc 两块盘都登记为 PV
  • vgcreate vg0 /dev/sdb /dev/sdc 组成卷组 vg0
  • lvcreate -L 10G -n lv0 vg0 切 10G 的 lv0
  • lvcreate -l 100%FREE -n lv1 vg0 剩余全给 lv1
  • mkfs.xfs /dev/vg0/lv0 格式化
查
  • pvs / vgs / lvs 三行速览
  • pvdisplay / vgdisplay / lvdisplay 详情
  • vgs 看 VFree(池子还剩多少)
bash · 两块盘 → 一个池子 → 一个 10G 逻辑卷
# ① 登记物理卷
pvcreate /dev/sdb /dev/sdc
# Physical volume "/dev/sdb" successfully created

# ② 组成卷组(池子)
vgcreate vg0 /dev/sdb /dev/sdc
# Volume group "vg0" successfully created
vgs
# VG   #PV #LV #SN Attr   VSize   VFree
# vg0    2   0   0 wz--n- 19.99g  19.99g   ← 池子约 20G 可用

# ③ 切出 10G 逻辑卷
lvcreate -L 10G -n lv0 vg0
# Logical volume "lv0" created.
lvs
# LV   VG   Attr       LSize  Pool Origin Data%  Meta%  Move
# lv0  vg0  -wi-a----- 10.00g

# ④ 格式化并挂载
mkfs.xfs /dev/vg0/lv0
mkdir -p /data
mount /dev/vg0/lv0 /data
df -h /data
LV 的设备路径是 /dev/卷组名/逻辑卷名,也可以写 /dev/mapper/vg0-lv0,两者等价。
5.4.3在线扩容:LVM 的看家本领

空间不够时的标准动作:池子不够先加盘(vgextend),池子够就直接扩逻辑卷(lvextend),最后一定要扩文件系统。少做最后一步,df 就不会变——这是新手最常踩的坑。

vgextend / lvextend / xfs_growfs★ 重点

扩容三步走。lvextend -r 可以顺便自动扩文件系统,省一步。

扩容命令
扩池子 / 扩卷
  • vgextend vg0 /dev/sdd 给池子加一块新盘
  • lvextend -L +5G /dev/vg0/lv0 给 lv0 加 5G
  • lvextend -l +100%FREE /dev/vg0/lv0 把池子剩余全给它
  • lvextend -r -L +5G ... 加 -r 自动扩文件系统
扩文件系统(关键)
  • xfs:xfs_growfs /data(对挂载点操作,必须已挂载)
  • ext4:resize2fs /dev/vg0/lv0(对设备操作)
  • 验证:df -h /data 容量变大才算成功
bash · 从 10G 在线扩到 15G
# ① 池子里还有空间,直接扩卷 +5G
lvextend -L +5G /dev/vg0/lv0
# Size of logical volume vg0/lv0 changed from 10.00 GiB to 15.00 GiB

# ② 扩文件系统(xfs 必须做这步,否则 df 还是 10G)
xfs_growfs /data
# data blocks changed from 2621440 to 3932160

# ③ 验证
df -h /data
# Filesystem           Size  Used Avail Use% Mounted on
# /dev/mapper/vg0-lv0   15G  1.2G   14G   9% /data   ← 变成 15G
xfs 只能扩不能缩;ext4 可缩但必须先卸载、e2fsck 检查,生产上尽量别缩。
bash · 池子也不够了:先加盘再扩
vgextend vg0 /dev/sdd        # 把新盘 sdd 加入池子
vgs                          # 确认 VFree 变大
lvextend -l +100%FREE /dev/vg0/lv0   # 剩余空间全给 lv0
xfs_growfs /data             # 扩文件系统
df -h /data
整个过程不用卸载、不用停机,业务无感知——这就是 LVM 相比裸分区的最大优势。
任务 5.5

故障排查:磁盘管理最常见的五个坑

会操作,更要会救火
5.5.1问题 → 定位 → 处理
现象定位命令原因与处理
磁盘满了,但 du 找不到大文件df -h → du -sh /* → lsof +L1文件被删除但仍被进程占用。找到进程后重启对应服务,空间才会释放。
容量没满却报 No space left on devicedf -iinode 耗尽(小文件太多)。清理缓存目录/邮件队列,或重新格式化时增大 inode 数。
umount 报 target is busyfuser -vm /data、lsof +D /data有进程或当前 shell 在该目录。退出目录、结束占用进程后再卸载。
fstab 写错,系统起不来启动时看报错、进救援模式mount -o remount,rw / 后注释错误行;以后改完先 mount -a 验证。
分区建好但 /dev/sdb1 不存在lsblk、dmesg | tail内核未重读分区表。执行 partprobe /dev/sdb。
💡
排错通用心法:先 df 看整体、再 du 下钻、然后 lsblk -f 看结构、最后 dmesg | tail 看内核报错。四条命令覆盖 80% 的磁盘问题。

拓展训练

教材每项目末尾 · 上机可重做取最高分
1
画图题:不看本页,默画「块设备 → 分区 → 文件系统 → 挂载点 → 目录树」五层链路,并在每层旁写出至少一条命令。
2
看盘:用 lsblk -f 画出当前机器的磁盘结构(哪些盘、几个分区、各自文件系统与挂载点),再用 df -hT 与 df -i 说明哪个分区最该关注。
3
全流程:给新盘 /dev/sdb 分区(10G)→ 格式化为 xfs → 挂到 /data → 用 UUID 写进 /etc/fstab → mount -a 验证 → 重启确认自动挂载。
4
配额:对用户 stu01 在 /data 上设 xfs 配额:容量软限 200M、硬限 250M,文件数硬限 1000;用 dd 写 300M 验证被拦截,并截图 xfs_quota report。
5
RAID:用 /dev/sdb、/dev/sdc 建 RAID 1(/dev/md0),格式化挂载后,用 --fail 模拟一块盘损坏,观察 /proc/mdstat 的 [UU] → [_U] 变化,再换盘重建回 [UU]。
6
LVM 扩容:把 /dev/sdb、/dev/sdc 做成 PV 聚成 vg0,切 10G 的 lv0 挂到 /data;再从 /dev/sdd 加盘进池子,在线扩到 15G,用 df -h 证明容量真的变了。

磁盘管理操作有风险,务必在虚拟机快照后练习;mkfs / mdadm --create / pvcreate 会清空数据。每道题都要留下「命令 + 关键输出 + 一句解释」的实验记录。