7.1 认识 Linux 文件系统

文件系统是建在磁盘上的。Linux 最传统的文件系统是 EXT2,CentOS 7 开始默认改用 XFS。本章从 EXT2 入手讲原理,再对比 XFS。

7.1.1 磁盘组成与分区的复习

HDD 机械硬盘

  • 物理组成:盘片 + 机械手臂(磁头)+ 主轴马达
  • 扇区(Sector):最小物理储存单位(512B 或 4K)
  • 磁道(Track)/ 柱面(Cylinder):扇区组成的同心圆 / 多盘片同一磁道的集合
  • 分区表:MBR(老旧、≤2TB)和 GPT(新、支持大容量)。MBR 第一个扇区 = 446B 开机区 + 64B 分区表
  • 磁盘文件名:实体磁盘 /dev/sd[a-p],分区 /dev/sda[1-128];虚拟磁盘 /dev/vd[a-p];LVM 用 /dev/VGNAME/LVNAME
  • CentOS 7 分区最小单位已从柱面改为扇区,默认强制 GPT

SSD 固态硬盘

上述内容描述的是传统 HDD(机械硬盘) 的物理结构,至今原理没变——盘片 + 机械臂 + 主轴马达,只是密度和容量在涨(HAMR、MAMR 等技术已推到 30TB+)。但现在的电脑主力已经是 SSD(固态硬盘),结构完全不同。

SSD 的结构

SSD 是纯电子设备,没有机械部件。核心组件:

  • NAND Flash 芯片:实际存数据的地方,类似 U 盘的闪存但性能和寿命强得多
  • 控制器(Controller):SSD 的大脑,负责管理数据读写、磨损均衡(wear leveling)、垃圾回收(GC),以及最重要的事——FTL(Flash Translation Layer),把文件系统看到的 LBA 地址映射到 NAND 上的实际物理位置
  • DRAM 缓存:部分 SSD 有独立缓存(类似 HDD 的 cache),加速随机写入;入门级 SSD 可能没有 DRAM,改用 HMB(Host Memory Buffer,借用系统内存)
SSD 的读写特点
  • 最小读写单位Page(通常 4K~16K)
  • 最小擦除单位Block(几十到几百个 Page 的集合)
  • 关键限制:不能直接覆盖。要修改一个 Page 的数据,必须先擦除整个 Block,再重新写入。如果 Block 里还有别的 Page 在用,控制器得先把它们搬到别处——这多出来的写入量就是”写入放大(Write Amplification)
  • TRIM:OS 删文件时通知 SSD “这些 Page 可以回收了”,让控制器提前擦除,避免写入时才发现没空位。用 SSD 必须确保 TRIM 已开启(Linux 在 /etc/fstabdiscard 或定期跑 fstrim
HDD vs SSD 对比
HDDSSD
存储介质磁性盘片NAND Flash 芯片
机械部件有(盘片旋转、磁头寻道)无,纯电子
怕震动
随机读写慢(磁头要物理移动)快(直接电信号寻址)
最小读写单位扇区(512B / 4K)Page(4K~16K)
覆盖写入直接覆盖需先擦除 Block(写入放大)
碎片问题有,需要重组基本不需要,但需开 TRIM
寿命机械磨损(很长)NAND cell 有 P/E 次数限制,但日常用很难写坏

文件系统层面的 inode/block 原理对 HDD 和 SSD 都适用——那是文件系统管的事,跟介质无关。

分区表

分区表和存储介质无关——不管 HDD 还是 SSD,都要靠分区表(MBR 或 GPT)告诉 OS “哪些扇区属于哪个分区”。前面 HDD 小节已提了一行,MBR 与 GPT 的详细结构(每条记录的字段拆解、限制来源、对比)已整合到 02-主机规划与磁盘分区 > 2.2.2 MSDOS(MBR)与 GPT 磁盘分区表,这里不再重复。

存储接口协议:SATA / SAS / NVMe

HDD/SSD 是”里面存数据的介质”,接口协议是”怎么跟主板通信”,两者独立——同一个 SATA 口可以插 HDD 也可以插 SSD。

接口面向带宽典型介质
SATA消费级(PC/笔记本)6GbpsHDD / SSD 都有
SAS企业级(服务器/存储阵列)12~24GbpsHDD / SSD 都有
NVMe高性能(直连 PCIe)32Gbps+几乎全是 SSD
  • SATA:最常见的消费级接口,所有主板都有。HDD 和入门 SSD 用
  • SAS:SATA 的企业级亲戚,可靠性更高、支持级联多盘。个人 PC 基本见不到,需要专用控制器。SAS 背板兼容 SATA 盘(反过来不行)
  • NVMe:走 PCIe 通道,比 SATA/SAS 快一个数量级。现代 SSD 的主力接口。设备文件是 /dev/nvme* 而非 /dev/sd*
  • 设备文件名:SATA/SAS 盘 → /dev/sd*;NVMe 盘 → /dev/nvme*;跟盘是 HDD 还是 SSD 无关

7.1.2 文件系统特性

格式化 = 创建一个 OS 认得的文件系统结构(每种 OS 的文件系统不同:Windows 用 FAT/NTFS,Linux 用 EXT2/XFS)。

一个可被挂载的数据单位称为”文件系统”——因为 LVM/RAID 技术可以将一个分区格式化为多个文件系统,或将多个分区分成单个文件系统,所以”一个 partition = 一个 filesystem”的说法已经过时了。

文件系统将数据分两类存放:

区块存放内容
superblock整个文件系统的总信息:inode/block 总量、使用量、剩余量、格式等
inode文件属性(权限、拥有者、时间戳)+ 该文件数据所在的 block 号码
data block文件的实际内容

这种”inode → block 号码”的方式叫索引式文件系统(indexed allocation),效率高——知道 inode 就能一口气找到所有 block。

对比:U 盘的 FAT 文件系统没有 inode,block 号码一个个链在前一个 block 里(“1→7→4→15”),文件分散时就得多转好几圈才能读完——这就是为啥 Windows 需要”磁盘重组”,而 EXT2/XFS 基本不需要。

7.1.3 Linux 的 EXT2 文件系统(inode)

EXT2 不会把所有 inode 和 block 堆在一起(大到没法管),而是分成多个区块群组(block group)——每个 block group 有自己独立的 inode/block/superblock 体系,像军营里分成多个连。

每个 block group 包含 6 个区域:

  • data block(数据区块):存放文件内容。block 大小有 1K/2K/4K,格式化后固定。一个 block 最多放一个文件的 data,所以小文件 + 大 block = 浪费空间。如 block=4K、放了 10000 个 50B 的小文件,实际不到 1MB 却浪费近 40MB。
  • inode table:每个 inode 固定 128B(EXT4 可 256B)。记录文件的 rwx 权限、owner、时间戳(ctime/atime/mtime)及指向 data block 的指针。一个文件占一个 inode,所以文件系统能创建的文件数 = inode 数量。
  • superblock(超级区块):记录 inode/block 总/用/余量、block 与 inode 大小、挂载时间、valid bit(0=已挂载,1=未挂载)。没有 superblock 就没有这个文件系统。后续 block group 可能含有 superblock 备份
  • block bitmap(区块对照表):哪个 block 被用了、哪个是空的——新增/删除文件时更新。
  • inode bitmap(inode 对照表):同上,记录 inode 使用状况。
  • Filesystem Description:描述每个 block group 的起止 block 号码。

inode 怎么记录大文件的 block? inode 只有 128B,记 block 号码一个要 4B。方案:

记录方式说明1K block 时可寻址
12 个直接指向直接存 block 号12K
间接用一个 block 装额外的 block 号(1K/4B=256 个)256K
双间接再套一层256×256K
三间接再套一层256³K

EXT4 的 inode 已扩展到 256B,支持单一文件 16TB、文件系统总容量 1EB。

dumpe2fs 可以查看 EXT 家族的 superblock 详情。

7.1.4 与目录树的关系

  • 目录:分配一个 inode + 至少一个 block。block 里记的是”文件名 → inode 号码”的对照表。目录下文件太多、一个 block 装不下时就会多分几个 block。
  • 文件:分配一个 inode + 对该文件大小的 block 数量。
  • inode 不记文件名——文件名存在目录的 block 里!这就是为什么增/删/改名文件需要目录的 w 权限(要改目录的 block 内容)。

读取 /etc/passwd 的流程

  1. 通过挂载信息找到 / 的 inode → 有 r+x 权限 → 读 / 的 block
  2. 在 / 的 block 里找到 etc/ 的 inode 号码 → 有 r+x 权限 → 读 etc/ 的 block
  3. 在 etc/ 的 block 里找到 passwd 的 inode 号码 → 有 r 权限 → 读 passwd 的 block

另外,文件系统规划太大容易导致 block 离散、磁头移动幅度过大,反而降低性能——分区不是越大越好

7.1.5 EXT2/EXT3/EXT4 文件的存取与日志式文件系统的功能

新增文件的流程

  1. 确认使用者对目录有 w+x 权限
  2. 通过 inode bitmap 找空 inode → 写入权限/属性
  3. 通过 block bitmap 找空 block → 写入数据 → 更新 inode 的 block 指向
  4. 同步更新 inode/block bitmap 和 superblock

数据不一致(Inconsistent)问题:如果步骤 4 做完之前断电,实际数据(inode table + data block)和元数据(bitmap + superblock)就对不上了。旧的 EXT2 需要开机时用 e2fsck 做全盘一致性检查——100GB 数据要查很久。

日志式文件系统(Journaling) 解决此问题:在文件系统中划一个日志区,三步走:

  1. 预备:在日志区记下”准备写文件 X”
  2. 实际写入:写入权限和数据,更新 metadata
  3. 结束:完成后在日志区标记完成

出问题时只查日志区就能定位哪份文件有问题,不用全盘扫描。EXT3/EXT4 就是 EXT2 + 日志,向下兼容(dumpe2fs 输出里能见到 Journal size 约 32MB)。

7.1.6 Linux 文件系统的运行

异步处理(Asynchronously):文件被编辑后,改动先留在内存(标记为 Dirty),系统不定时批量写回磁盘,或手动 sync。这样做是因为内存比磁盘快太多——所以:

  • 常用文件会被缓存到内存,Linux 实体内存最后会被用光是正常的(不是内存泄漏)。
  • 正常关机时会自动 sync;异常断电 → Dirty 数据丢失 → 开机要花时间磁盘检验,甚至 damage 文件系统。

7.1.7 挂载点的意义(mount point)

挂载 = 将文件系统链接到目录树的某个目录。该目录即为进入该文件系统的”入口”。

同一个文件系统的顶层 inode 号通常相同(如 XFS 都是 128 号),所以检查 ls -lid / /boot /home 的 inode 可以判断它们是否属于同一个文件系统——inode 号相同 → 不同文件系统。根目录的 ... inode 相同(都是根目录自己的 inode),因为 /.. 就是 /

7.1.8 其他 Linux 支持的文件系统与 VFS

Linux 支持的文件系统非常多:

  • 传统:ext2、minix、FAT(vfat 模块)、iso9660(光盘)
  • 日志式:ext3/ext4、ReiserFS、NTFS、JFS、XFS、ZFS
  • 网络:NFS、SMBFS

VFS(Virtual Filesystem Switch):Linux 核心通过 VFS 这一层来管理所有文件系统,使用者和应用程序不需要关心每个 partition 是什么格式——VFS 自动处理读取,对外提供统一接口。

7.1.9 XFS 文件系统简介

CentOS 7 换 XFS 的原因:EXT 家族格式化时要预先分配所有 inode/block/metadata,TB 以上磁盘格式化极慢。XFS 的 inode/block 用时才动态分配,格式化几秒搞定。

XFS 分三个区:

  • 数据区(data section):类似 EXT 的 block group,分多个 allocation group(AG)。inode/block 动态产生。block 支持 512B64K(但 Linux 限于 memory page 大小只能 4K),inode 256B2M。
  • 活动登录区(log section):日志区,可指定外部设备(如 SSD)当日志,提升性能。
  • 实时运行区(realtime section):创建文件时先在这里分配 extent,再写入 data section。extent 大小 4K~1G,普通磁盘默认 64K。

xfs_info 查看 XFS 文件系统详情(类似 EXT 的 dumpe2fs)。

7.2 文件系统的简单操作

7.2.1 磁盘与目录的容量

df:列出文件系统整体使用量(读 superblock,速度快)

  • df -h:人类可读格式
  • df -T:同时显示文件系统类型
  • df -i:以 inode 为单位显示(有的场景 block 还剩很多但 inode 用光了,也无法创建文件)
  • df 目录名:自动分析该目录所在分区的容量

du:评估目录/文件占用空间(直接扫描文件系统,速度慢)

  • du -sm /*:用 MB 列出各根目录占用,常用于找”谁占的容量最大”
  • du -s:只列总量
  • du -S:不含子目录的总计

教训:磁盘总空间还有,但某个分区满了就没法往里写数据。尤其留意 /var/spool/mail 容易被灌爆。

7.2.2 实体链接与符号链接:ln

回顾关键概念:文件名存在目录的 block 里,inode 是文件的”身份证”

Hard Link(实体链接)

  • 原理:在某个目录的 block 里新增一条记录,指向同一个 inode 号
  • ls -l 的第二字段(链接数)会增加
  • 创建 hard link 不增加 inode 和 block(只多一条目录记录)
  • 删除任何一个文件名,实际数据还在(因为 inode 还在)
  • 限制:不能跨文件系统、不能 link 目录(否则会造成循环引用,异常复杂)

Symbolic Link(符号链接/捷径)

  • 原理:创建一个新的独立文件,内容存放目标文件的”文件路径”
  • ls -l 显示文件名 → 目标文件,文件大小 = 目标路径的字符数
  • 会占用 inode + block(是新文件)
  • 原文件被删除后,符号链接就”断掉”了(No such file or directory)
  • 可以跨文件系统、可以链接目录
  • 修改符号链接文件实际上修改的是原始文件
ln 源文件 目标文件        # hard link(不加参数)
ln -s 源文件 目标文件     # symbolic link

目录的 link 数:空目录的 link 数为 2(目录本身 + 目录内的 .),上层目录的 link 数 + 1(因为子目录里的 .. 指向上层)。

💡 Hard link 限制太多(不能跨 FS、不能 link 目录),实际多用 Symbolic link,但它就是”指针”,原文件没了就废了——各有利弊。

7.3 磁盘的分区、格式化、检验与挂载

新增磁盘的四步:分区 → 格式化 → 检验 → 挂载

7.3.1 观察磁盘分区状态

指令用途
lsblk列出所有储存设备的树状结构(磁盘 + 分区 + 挂载点)
blkid列出每个设备的 UUID、文件系统类型(TYPE)
parted /dev/xxx print查看分区表类型(MBR/GPT)和各分区详情

UUID 是全域唯一识别码,比设备文件名可靠——同一个 USB 盘插不同机器设备名可能变(sda→sdb),但 UUID 不变。

7.3.2 磁盘分区:gdisk / fdisk

关键规则:MBR → 用 fdisk;GPT → 用 gdisk。搞反会破坏分区表!

gdisk 操作要点:

  • ? 查看帮助,p 打印分区表,n 新增分区,d 删除分区
  • q = 放弃修改退出,w = 写入生效并退出(只要不按 w,随便玩没事)
  • 分区参数用 +1G+500M 这样的格式指定大小(不用自己算扇区号)
  • 创建完分区后用 partprobe -s 更新核心分区表(否则新分区不出现)
  • 绝对不要处理正在使用中的文件系统

fdisk 操作类似,只是按 m 看帮助,且 MBR 有 Primary/Extended/Logical 的复杂规则。

7.3.3 磁盘格式化(创建文件系统)

格式化 = make filesystem = mkfs

  • XFSmkfs.xfs /dev/xxx(快,秒级完成)。可调参数:-b block 大小、-d agcount=N(AG 数建议匹配 CPU 核心数)、-L 设置标头名。
  • EXT4mkfs.ext4 /dev/xxx-b 设置 block 大小(1K/2K/4K)。
  • 综合指令mkfs -t xfs /dev/xxx(自动调用对应的 mkfs.xxx)
  • mkfs[tab][tab] 查看系统支持的所有格式化工具

RAID 性能优化(可选):文件系统格式化时指定 stripe 参数(su/sw),让文件系统和 RAID 卡对齐,避免重复切分,性能更好。第 14 章详解。

7.3.4 文件系统检验

  • XFSxfs_repair /dev/xxx文件系统必须处于卸载状态! 根目录出问题时需单人维护模式加 -d 选项强制修复。
  • EXT4fsck.ext4 /dev/xxx-f 强制检查(正常 clean 状态跳过),-b superblock号 用备份 superblock 救援。block=4K 时备份 superblock 在 32768 号位置。

7.3.5 文件系统挂载与卸载

挂载前须知

  • 一个文件系统不应重复挂载到多个挂载点
  • 一个目录不应重复挂载多个文件系统
  • 挂载点应该是空目录(否则原内容暂时被隐藏,卸载后恢复)

mount

mount UUID="xxx" /挂载点         # 推荐方式
mount 设备文件名 挂载点           # 传统方式
mount -a                          # 按 /etc/fstab 挂载所有
mount -o remount,rw,auto /        # 重新挂载(改参数)
mount --bind 源目录 目标目录       # 挂载目录到另一个位置

挂载特殊文件系统

  • 光盘mount /dev/sr0 /data/cdrom(自动只读;挂载后无法物理退盘,需先 umount
  • U 盘(VFAT):挂载时加 -o codepage=950,iocharset=utf8 处理中文文件名
  • NTFS:CentOS 7 默认不支持,需额外装驱动(第 22 章)

系统用 /etc/filesystems/proc/filesystems 决定挂载测试顺序,自动识别文件系统类型。

umount

umount 设备文件名     # 设备名或挂载点都可
umount 挂载点         # 更好记

卸载失败(target is busy)= 有人在用该文件系统(可能是你 cd 进去了),cd / 出来后就能卸载。

7.3.6 磁盘/文件系统参数修订

  • mknod:手动创建设备文件 mknod /dev/xxx b/c/p [Major] [Minor](现代系统自动产生,一般不需要)
  • xfs_admin:修改 XFS 的 Label name 和 UUID(-L 改名称,-U 改 UUID)
  • tune2fs:修改 EXT4 的 Label name 和 UUID
  • uuidgen:生成新的随机 UUID

7.4 设置开机挂载

7.4.1 开机挂载 /etc/fstab 及 /etc/mtab

/etc/fstab 是开机挂载的配置文件,系统启动时 mount -a 会按此文件挂载所有文件系统。每行六个字段:

设备/UUID  挂载点  文件系统类型  挂载参数  dump备份  fsck检查顺序
  • 挂载参数:defaults = rw, suid, dev, exec, auto, nouser, async
  • 第四字段(dump):0=不备份
  • 第五字段(fsck):0=不检查,1=根目录先查,2=其他

/etc/mtab:实时记录当前已挂载的文件系统(由 mount/umount 自动更新)。

7.4.2 特殊设备 loop 挂载

挂载镜像文件(ISO 等),不烧盘也能用

mount -o loop 镜像文件.iso /挂载点

原理:通过 loop 设备将文件”伪装”成块设备来挂载。适用于 ISO 镜像、磁盘映像文件等。

7.5 内存交换空间(swap)之创建

Swap 是当实体内存不够时,把暂时不用的内存数据换到磁盘上的机制(以 partition 或 file 形式)。

7.5.1 使用实体分区创建 swap

  1. 用 gdisk 创建一个分区(type code = 8200)
  2. mkswap /dev/xxx 格式化为 swap
  3. swapon /dev/xxx 启用
  4. 写入 /etc/fstab 实现开机自动启用
  5. free -hswapon --show 查看 swap 使用情况

7.5.2 使用文件创建 swap

  1. dd if=/dev/zero of=/swapfile bs=1M count=512(创建 512MB 的空文件)
  2. mkswap /swapfile
  3. swapon /swapfile
  4. 写入 /etc/fstab

相比分区方式,文件方式更灵活(随时可调整大小),但性能略逊。

7.6 文件系统的特殊观察与操作

7.6.1 磁盘空间之浪费问题

block 大小导致的空间浪费(7.1.3 已讲)。此外,inode 数量在格式化时就固定了——有可能block 还有很多空间,但 inode 用光了导致无法创建文件。用 df -i 检查 inode 使用率。

7.6.2 利用 GNU 的 parted 进行分区行为

parted 是能同时处理 MBR 和 GPT 的分区工具,比 fdisk/gdisk 更强大但命令更直接(没有交互保护):

  • parted /dev/xxx print:查看分区表
  • parted /dev/xxx mkpart primary xfs 0% 50%:创建分区
  • parted /dev/xxx rm 3:删除 3 号分区

用 parted 要格外小心——没有”q 不保存”这种安全网,命令执行即生效。

关键命令

命令用途
dumpe2fs -h 设备查看 EXT superblock
xfs_info 挂载点查看 XFS superblock
df -hTi查看文件系统用量+类型+inode
du -sm 目录评估目录占用
ln [-s] 源 目标创建 (符号) 链接
lsblk列出所有储存设备
blkid列出设备 UUID 和文件系统类型
parted /dev/xxx print查看分区表
gdisk /dev/xxxGPT 分区工具
fdisk /dev/xxxMBR 分区工具
partprobe -s更新核心分区表
mkfs.xfs / mkfs.ext4格式化
xfs_repair / fsck.ext4文件系统修复
mount [-o 参数] 设备 挂载点挂载
umount 挂载点卸载
mkswap / swapon / swapoffswap 创建/启用/关闭
sync强制将内存 Dirty 数据写回磁盘
uuidgen生成 UUID
xfs_admin / tune2fs修改 Label/UUID