☰
Linux磁盘分区格式化挂载全链路:MBR/GPT选型与实战避坑
2026/10/27 7:31:17 网站建设 项目流程

磁盘分区、格式化、挂载这几件事,听着像运维入门第一课,但真到了生产环境,一次手抖就能把整块盘的数据送走。我这些年经手的机器,从几十GB的虚拟机磁盘到十几TB的直连存储阵列都碰过,踩过的坑基本都集中在三个地方:分区表类型选错、文件系统参数拍脑袋、fstab 写错导致机器起不来。这篇就围绕 MBR/GPT 分区、格式化、挂载这条完整链路,把每一步背后的判断逻辑讲清楚,附上可以直接抄的命令和参数,也会把几个真实翻车现场拆开给你看。不管你是刚接手一台新服务器不知道从哪下手,还是已经能敲 fdisk 但说不清为什么这么选,应该都能从里面捞到点东西。

1. 先搞清楚 MBR 和 GPT 到底差在哪

很多人知道"超过 2TB 用 GPT",但再往下问一句为什么是 2TB,就答不上来了。这个数字不是拍出来的,是 MBR 分区表结构决定的硬边界。理解这一点,后面所有的选型判断都会变得很自然。

1.1 从 512 字节说起:MBR 的物理边界

MBR 全称主引导记录,它就住在磁盘的第一个扇区里,一共 512 字节。这 512 字节被切得很死:前 446 字节是引导代码,中间 64 字节是分区表,最后 2 字节是结束标志 0x55AA。也就是说,真正用来描述分区的只有 64 字节。

64 字节要描述几个分区?每一条分区记录占 16 字节,所以最多 4 条。这就是"MBR 最多 4 个主分区"的由来,不是什么设计偏好,是字节数不够用。后来为了突破这个限制,才有了扩展分区的概念——把其中一个主分区的位置让出来,做成一个可以继续往下挂逻辑分区的"容器"。这个方案能用,但本质上是打补丁,逻辑分区的编号从 5 开始,链条一长就容易出问题,一旦扩展分区的链表断了一环,后面的分区就全部失联。

再看 16 字节怎么分配:1 字节状态位、3 字节起始 CHS、1 字节分区类型、3 字节结束 CHS、4 字节起始 LBA、4 字节分区扇区总数。关键在最后那个 4 字节的扇区总数——4 字节是 32 位,最大能表示 2^32 个扇区。传统扇区是 512 字节,乘一下就是 2^32 × 512 = 2199023255552 字节,约等于 2TiB。这就是那道墙的来源。

注意:这个上限说的是"单个分区的容量",不是"整块盘的容量"。一块 4TB 的盘用 MBR,你完全可以切成两个 2TB 以内的分区正常使用,只是没法切出一个超过 2TiB 的分区。这一点经常被混淆。

还有一层历史包袱:CHS 寻址。早期的硬盘用柱面/磁头/扇区三维坐标定位,CHS 三个字段加起来能表达的扇区数有上限,大约 8GB 左右。现代系统早就改用 LBA 线性寻址了,但 MBR 结构里那 6 个字节的 CHS 字段还留着,属于兼容性遗产。你在 fdisk 里看到的那些警告,很多就跟这个有关。

1.2 GPT 的补救方案:LBA 布局与备份表

GPT 是 GUID 分区表,它没有去改 MBR 那 512 字节,而是另起炉灶重新设计了一套布局,同时保留了向前兼容的能力。

GPT 的磁盘布局是这样排的:LBA0 放一个"保护性 MBR",里面只有一条类型为 0xEE 的分区记录,覆盖整块盘。这条记录的作用是让不认识 GPT 的老工具看到之后,认为这块盘已经被占满了,从而不会误操作去覆盖 GPT 结构。LBA1 是 GPT 头,记录了自己的位置、备份头的位置、分区表项的数量和大小、CRC32 校验值、磁盘 GUID 等元信息。LBA2 到 LBA33 这 32 个扇区存放真正的分区表项数组,默认 128 条,每条 128 字节,一共 16KiB。磁盘末尾还完整保留了一份备份,包括备份头和备份分区表项。

这个"头 + 表 + 备份"的三段式设计,带来的直接好处是容错。如果开头的 GPT 头被写坏了,gdisk 或者 sgdisk 可以从磁盘末尾的备份恢复回来,命令大致是sgdisk -e /dev/sdb重建备份,或者用gdisk的恢复菜单从备份表重建主表。我遇到过不止一次因为误操作 dd 把开头几 MB 覆盖掉的情况,都是靠尾部备份救回来的。

容量上限方面,GPT 头里用 8 字节描述 LBA 地址,2^64 个扇区 × 512 字节,理论上限在 9.4ZB 这个量级,实际中你根本碰不到。分区数量默认 128 个,这个数字可以调大,但没必要,128 个已经远超常规需求。

GPT 分区项里存的不是分区类型编号,而是分区类型 GUID。比如 Linux 文件系统是 0FC63DAF-8483-4772-8E79-3D69D8477DE4,EFI 系统分区是 C12A7328-F81F-11D2-BA4B-00A0C93EC93B,Linux LVM 是 E6D6D379-F507-44C2-A23C-238F2A3DF928。另外还有 36 字符的分区名和 36 字符的分区 GUID。分区名这个东西在 MBR 里是没有的,非常实用——你可以给每个分区起个名字,用lsblk -o NAME,PARTLABEL直接看出来这块盘是干嘛的,不用靠记忆。

1.3 一张表看清选型边界

把两者的差异拉平了对比,选型的时候就不容易犹豫。下面这张表是我自己整理的一份速查,贴在笔记本上的那种。

对比项MBRGPT
占用空间1 个扇区(512B)34 个扇区 + 末尾备份
分区表容量64 字节默认 16KiB
最大主分区数4128(可调)
扩展分区支持,最多 1 个不需要这个概念
单分区最大容量2TiB约 9.4ZB
分区标识1 字节类型码16 字节类型 GUID
分区名称无支持,36 字符
校验机制无CRC32
备份无磁盘尾部完整备份
启动兼容性传统 BIOS 通吃需要 UEFI,或 BIOS+GPT 引导分区

选型上有几条经验值得记住。系统盘如果机器是 UEFI 启动,那没得选,必须是 GPT,而且必须有一个 EFI 系统分区。数据盘如果单块容量超过 2TiB,也别犹豫,直接 GPT。剩下那些小容量盘、老旧设备、需要跟某些老系统兼容的场景,MBR 依然能用,也不会有什么问题。

真正麻烦的是"BIOS 启动 + GPT 磁盘"这个组合。它技术上可行,但需要在磁盘上单独划一个 1MB 左右的 bios_grub 分区,用于存放 GRUB 的 core.img。如果你用 parted 建 GPT 盘然后装系统,没划这个分区,装完就起不来,报错通常是一句很含糊的 "grub-install failed"。这个坑我踩过,在那之后我形成了一个习惯:只要盘是 GPT 且不确定启动方式是啥,先问清楚,或者干脆留 2MB 空闲空间。

2. 动手之前:把四层概念捋顺

很多操作事故的根源不是命令敲错了,是心里的模型不对。把磁盘、分区表、分区、文件系统这四个层级分清楚,后面每一步做什么、作用在哪一层,就不会混淆。

2.1 磁盘、分区表、分区、文件系统

从左到右,这是一个层层嵌套的关系。最底下是块设备本身,比如 /dev/sdb,它就是一堆可以按扇区随机读写的存储空间。往上一层是分区表,MBR 或者 GPT,它是一份"目录",记录着这块盘被切成了几块、每块从哪个扇区到哪个扇区。再往上是分区,/dev/sdb1、/dev/sdb2 这些,它们是逻辑上的连续扇区区间。最上面是文件系统,ext4、XFS、NTFS 这些,负责管理文件、目录、元数据、空闲块。

这个层级关系解释了太多现象。比如为什么删掉分区数据还在?因为删分区只是改了分区表那份"目录",扇区里的文件系统结构原封不动,所以用 testdisk 之类的工具扫描还能把分区表重建出来。反过来,为什么格式化之后数据就难恢复了?因为 mkfs 往分区开头写了一整套新的元数据结构,把原来的索引全冲了。

再比如为什么分区表改了内核却不认?因为分区表是给内核看的,内核在启动时读一次缓存起来,你改了之后需要触发重读,用 partprobe 或者 partx,或者最粗暴的重启。这也是个常见卡点,后面会细说。

还有一个容易混淆的点:分区和文件系统不是一对一绑定的。你完全可以在一个分区上叠 LVM 物理卷,再在上面切逻辑卷,再在逻辑卷上建文件系统;也可以不分区,直接在整块盘上 mkfs(虽然不推荐,但技术上没问题);ZFS、Btrfs 这类还支持自己管理多块盘。所以看到"分区"这个词的时候,先确认它指的是哪种语境。

2.2 设备命名怎么读

/dev/sda、/dev/sdb 这种命名,sd 后面的字母是按设备被内核识别的顺序分配的,a 是第一块,b 是第二块,依此类推。超过 26 块会变成 sdaa、sdab。数字后缀表示分区号,sdb1 是第一分区,sdb2 是第二。

这里有个必须警惕的点:sda、sdb 的分配顺序不稳定。插拔一块盘、换一个 HBA 卡、改一下 BIOS 启动顺序,都可能让原来的 sdb 变成 sdc。如果你在 fstab 里写的是 /dev/sdb1,某次重启之后它挂到了别的盘上,轻则挂载失败,重则把本该写数据的分区覆盖掉。这就是为什么生产环境一律用 UUID 或者 LABEL 挂载,后面专门讲。

NVMe 设备是另一套命名,/dev/nvme0n1 表示第 0 个控制器的第 1 个命名空间,分区是 /dev/nvme0n1p1,注意这个 p 不能省,因为 n1 后面直接跟数字会有歧义。虚拟化环境里还常见 /dev/vda、/dev/xvda,道理一样。

盘多了记不住怎么办?lsblk 是我最常用的,一条lsblk -o NAME,SIZE,TYPE,FSTYPE,MOUNTPOINT,PARTLABEL,MODEL,SERIAL能把整棵树的层级、容量、类型、文件系统、挂载点、分区名、型号、序列号全列出来。看输出的时候注意 TYPE 列,disk 是整盘,part 是分区,lvm 是逻辑卷,rom 是光驱。这个信息量比 fdisk -l 友好得多。

2.3 工具选型:fdisk、parted、gdisk 各管什么

这三个工具都能分区,但脾气不一样,用错场景会给自己添堵。

fdisk 是老牌工具,传统上只管 MBR。新版 util-linux 里的 fdisk 已经支持 GPT 了,输入 n 新建分区时会自动按当前分区表类型来走。它的界面是交互式的,一个字母一个操作,熟悉之后效率很高。缺点是脚本化麻烦,虽然可以用echo -e "n\np\n1\n\n\nw" | fdisk这种管道喂输入,但很容易出错,我不太推荐在自动化脚本里这么干。

parted 是通用型,MBR 和 GPT 都能建,而且支持非交互模式,一条命令搞完,非常适合写进脚本。它的一个特点是对齐处理得很聪明,默认按 1MiB 边界对齐,省得你自己算。缺点是它有些操作是立即生效的,没有"确认"这一步,手一滑就完了。用 parted 之前一定要用parted /dev/sdb print先看清楚现状,确认盘选对了。

gdisk 是 GPT 专用,界面长得像 fdisk,但对 GPT 的理解最到位,能处理类型 GUID、分区名、备份表恢复这些细节。如果你的盘确定是 GPT,用 gdisk 比 fdisk 顺手。sgdisk 是它的非交互版本,适合脚本。

我自己的习惯是:交互式操作、盘不大、情况清楚的时候用 fdisk 或 gdisk,看分区表类型决定;写脚本、要批量处理、需要精确控制对齐和类型的时候用 parted 或 sgdisk。这个分工不是硬规定,顺手最重要。

还有两个不能忽略的辅助工具。lsblk 用来总览全局,前面说过了。blkid 用来查分区上的文件系统信息,包括 UUID、TYPE、LABEL,挂载配置的时候离不开它。另外还有一个 wipefs,用来擦除分区上的文件系统签名,当你需要彻底"清空"一块盘、或者遇到内核总把签名识别错的时候,wipefs -a /dev/sdb1能干净利落地解决问题,比 dd 覆盖整盘快得多。

3. 分区实操:从裸盘到可用分区

理论说完,直接上手。下面分别走一遍 MBR 和 GPT 的完整流程,用的是虚拟机里的测试盘,容量和命名跟你在生产环境里遇到的可能不同,但步骤完全一致。

3.1 用 fdisk 建 MBR 分区

假设新插了一块 500GB 的盘,被识别成 /dev/sdb。第一步永远是确认目标,不能省。

lsblk /dev/sdb fdisk -l /dev/sdb

看到输出里 Disk label type 是 dos,说明当前是 MBR。如果是空的、没有分区表,也可以直接从零开始。

fdisk /dev/sdb

进入交互界面后,按顺序操作:

  • p打印当前分区表,确认现状
  • o新建一个空的 MBR 分区表(会清空,慎用,没有分区表的时候才需要)
  • n新建分区,选择p主分区,分区号1,起始扇区直接回车走默认,结束扇区可以输入+100G这种相对大小或者+20G这种
  • 再n建第二个分区,注意起始扇区默认值会紧跟在第一个分区之后
  • p再打印一次,确认分区信息符合预期
  • t可以修改分区类型,比如设成 82 做 swap、83 是 Linux、8e 是 LVM
  • a可以用来设置启动标志位
  • 确认无误后w写入并退出

写入这一步会立刻修改磁盘上的分区表。fdisk 在 w 之前的所有操作都只在内存里,所以如果你中途发现搞错了,直接q退出,不会留下任何痕迹。这个设计很友好,但同时也意味着你不能"撤回已经 w 的操作",只能重新分。

这里有个细节值得说一下:fdisk 默认的起始扇区。老版本会从 2048 扇区(约 1MiB)开始,这是为了对齐。如果你看到起始扇区是 63,那多半是某些特殊场景或者老工具,现代磁盘上从 63 开始会导致分区跨在物理块边界上,性能会掉。所以如果 fdisk 给的默认值很怪,手动输入 2048 更稳妥。

3.2 parted 建 GPT 分区,顺带解决对齐

GPT 的场景下我更喜欢 parted,命令清晰,参数可控。同样先确认目标:

parted -s /dev/sdb print

如果提示 "unrecognised disk label",说明盘上没有有效分区表。开始操作:

# 建立 GPT 分区表 parted -s /dev/sdb mklabel gpt # 建第一个分区,1MiB 起,100GiB 止,设个名字 parted -s /dev/sdb mkpart data1 1MiB 100GiB # 建第二个分区,占满剩余空间 parted -s /dev/sdb mkpart data2 100GiB 100% # 设置分区类型标记,可选但推荐 parted -s /dev/sdb set 1 lvm on # 查看结果 parted -s /dev/sdb unit MiB print

这里的1MiB起始不是随便写的。传统机械硬盘的物理扇区是 512 字节,现代硬盘(尤其是 4Kn 盘和 SSD)物理扇区是 4096 字节。如果分区起始扇区不是 8 的整数倍(4096/512=8),那么每个文件系统的块都会横跨两个物理扇区,一次读操作要触发两次实际读写,性能损失在 SSD 上尤其明显。用 1MiB 作为起点,天然是 8 的倍数,也符合绝大多数 SSD 的擦除块大小,是最省心的选择。parted 默认就是这么对齐的,你只要别手动去填一个奇怪的数值就行。

注意:parted 的mkpart一旦执行就立即写入分区表,没有"未保存"这个中间状态。所以-s(script 模式)虽然方便,但也意味着没有任何提示就改了盘。我的做法是先不带-s跑一次print,看着输出确认是这块盘,再加-s执行。多敲一条命令,少一次事故。

3.3 4K 对齐到底怎么验证

对齐这个东西,光看命令返回值看不出来,得实际检查。parted 的 print 输出里有一列是 Start,单位可以指定成扇区:

parted /dev/sdb unit s print

看上图那种输出,如果 Start 是 2048、4096 这种 8 的倍数,就对齐了。如果出现 63、2047 这种,就有问题。另一个更直观的方法是查 /sys 下的对齐信息:

cat /sys/block/sdb/sdb1/start cat /sys/block/sdb/sdb1/alignment_offset

alignment_offset 是 0 就说明对齐良好。非 0 的话,说明分区起点相对于物理块边界有偏移。

已经分好的区如果没对齐,修复起来比较麻烦,基本上只能备份数据、删分区、重新按对齐的边界分、再恢复数据。所以这事最好在分区的时候就做对。也正因为如此,我不太建议在裸盘上手工指定扇区号去分区——除非你有明确的理由,否则让 parted 帮你按 1MiB 走,是最省事的。

顺便提一个容易忽略的点:分区结束位置。结束位置如果不是物理块的边界,最后一个块也会跨边界。parted 在计算100%的时候会自己处理,你用相对值就没事。手工填扇区号的时候要注意。

3.4 分区表刷新:为什么内核看不见新分区

这是新手最常卡住的地方。你分完区,兴冲冲地mkfs.ext4 /dev/sdb1,结果报 "No such file or directory"。原因是内核还在用它启动时缓存的旧分区表。

解决方式有几种,按推荐顺序:

# 方式一:partprobe,最温和 partprobe /dev/sdb # 方式二:partx 按盘刷新 partx -u /dev/sdb # 方式三:blockdev 让内核重读 blockdev --rereadpt /dev/sdb # 方式四:重启,最粗暴但最有效

partprobe 在绝大多数情况下够用。它失败通常是因为某个分区正在被挂载使用,内核拒绝重读。这时候要么先 umount,要么就只能重启。生产环境里如果盘上有正在跑的业务,绝对不能为了刷新分区表去重启,得另外想办法,比如用 LVM 或者直接在已挂载的分区上扩容(ext4 和 XFS 都支持在线扩容,但前提是底层块设备变大了,这个流程后面讲)。

还有一种情况是分区表确实刷新了,但 /dev 下的设备节点没出来。这时候可以看一下ls /dev/sdb*,如果确实没有 sdb1,但cat /proc/partitions里有,那说明是 udev 的问题,udevadm trigger或者udevadm settle试一下。这种情况在容器环境或者精简系统里比较常见。

4. 格式化:文件系统选型与参数落地

分区建好了,接下来是格式化。这一步的参数选择直接影响后面的性能、可维护性和扩容能力,不是mkfs.ext4 /dev/sdb1一敲就完事的。

4.1 ext4 还是 XFS,别拍脑袋

这是被问得最多的一个问题。简单的判断标准是看场景,我给一个我自己在用的决策路径。

ext4 的优势在于成熟、工具链完整、支持缩小(虽然要卸载)、单文件系统在小到中等规模下表现稳定、在线扩容支持好。它的元数据操作相对轻量,大量小文件、频繁创建删除的场景下比 XFS 更从容一点。缺点是单文件系统在超大容量、超大文件并发写入的场景下,扩展性和性能不如 XFS。

XFS 的优势在于大容量、大文件、高并发 IO。它是 64 位文件系统设计的,天生适合大磁盘,元数据用 B+ 树组织,并行度高。缺点是不能缩小(只能扩容),而且早期版本对单文件系统的最小容量有要求(现在基本不是问题)。另外 XFS 的删除大量小文件在某些版本上会有性能抖动,这个要注意。

场景推荐理由
系统盘ext4工具链成熟,救援方便
通用数据盘 < 10TBext4够用,扩容缩容都灵活
大容量数据盘 > 10TBXFS扩展性和大文件性能更好
高并发写入、日志类XFS并行元数据操作
大量小文件ext4元数据开销更小
容器镜像层XFS(ftype=1)overlay2 依赖 d_type
需要缩容ext4XFS 不支持缩小
跨平台交换exFAT / NTFS兼容性优先

还有一个经常被忽略的选项是 Btrfs,快照、子卷、校验这些特性很香,但生产环境里我一般不会主动推,除非团队有明确的运维能力。至于 exFAT 和 NTFS,基本只用在需要跟 Windows 交换数据的移动盘上。

4.2 mkfs.ext4 的关键参数

基础命令当然简单,但几个参数值得展开说。

mkfs.ext4 -L data01 -m 1 -b 4096 -E stride=128,stripe_width=256 /dev/sdb1

-L设置卷标,这个后面挂载的时候可以用 LABEL= 引用。卷标建议起得有辨识度,比如 data01、backup02,别用默认的空值或者一堆数字,不然盘一多就分不清谁是谁。

-m是保留块百分比,默认 5%。这 5% 是留给 root 的,目的是防止普通用户把盘写满之后系统无法正常工作(比如日志写不进去)。但这个默认值在几十 GB 的盘上还行,在 10TB 的盘上就是 500GB 白白浪费。我的做法是大盘一律设成 1%,如果确定是纯数据盘、不会有系统进程写日志,设成 0 也可以。改保留块不需要重格式化,tune2fs -m 0 /dev/sdb1就能在线调整。

-b是块大小,默认 4096。除非有特殊理由(比如大量极小文件想用 1024),否则不要改。块大小一旦定了,后面调整很麻烦。

-E stride和stripe_width是给 RAID 用的。stride 等于阵列的条带大小除以文件系统块大小,stripe_width 等于 stride 乘以数据盘数量。比如一个 6 盘 RAID5,条带 64KiB,块 4KiB,那 stride = 64KiB / 4KiB = 16,stripe_width = 16 × 5(RAID5 数据盘数)= 80。这两个参数设对了,元数据写入才能按条带对齐,否则会产生读-改-写放大。如果底层没有 RAID,或者不确定,就别设,让它走默认。

还有-T预设,分 largefile、smallfile、largefile4 几档,本质上是帮你一次性设好 inode 密度等参数。largefile 系列的 inode 数量少,适合存大文件;默认的 smallfile 偏向海量小文件。如果你的盘上要存几百万个小文件,用默认就行;如果是视频、备份这类大文件为主,用-T largefile能省下可观的 inode 空间。

4.3 mkfs.xfs 的参数与 ftype 的坑

XFS 的命令长这样:

mkfs.xfs -f -L data02 -b size=4096 -n ftype=1 -d su=64k,sw=5 /dev/sdb2

-f是强制,当分区上已经有文件系统时 XFS 会拒绝执行,加-f才会覆盖。这个设计其实挺好,防误操作,但要注意它的存在。

-L同样是卷标。XFS 改卷标用xfs_admin -L newname /dev/sdb2。

-n ftype=1这个参数特别提一下。如果你打算在这块盘上跑容器(Docker、containerd),用 overlay2 存储驱动,那这个参数必须是 1。因为 overlay2 需要文件系统支持 d_type,而 XFS 在某些版本/配置下默认是不支持的。设成 1 之后,目录项里会记录文件类型,overlay2 才能正常工作。如果你忘了设,后期唯一的补救方式是备份数据、重新 mkfs、再恢复,非常折腾。所以只要盘可能用来跑容器,无条件带上这个参数。

-d su=64k,sw=5是条带参数,对应 RAID 的条带单元大小和数据盘数。跟 ext4 的 stride/stripe_width 是同一个道理,只是表达方式不同。su 就是条带单元,sw 是数据盘数量。注意 XFS 的 sw 在 RAID5/6 上填的是数据盘数,不含校验盘。算错了对性能没好处,不确定就别填。

XFS 还有一个必须知道的限制:不能缩小。xfs_growfs只能扩容,缩容没有任何官方支持。所以给 XFS 盘分区的时候,宁可先给小一点,后面再扩,不要一上来就占满。这个策略在很多场景下都成立——扩大容易缩小难,留点余量。

提示:mkfs 类操作会改写分区开头的元数据区域,原有数据基本无法通过常规手段恢复。执行前建议用lsblk -f和blkid双重确认目标分区,宁可多敲两条命令。

4.4 卷标、UUID 和它们的区别

格式化完成之后,分区上就有了三样身份标识:设备名、卷标、UUID。

设备名 /dev/sdb1 是最不可靠的,前面说过原因。卷标是人为设置的可读字符串,好处是好记,坏处是可能重复——你完全可以把两块盘的卷标都设成 data01,系统不会拦你,但挂载的时候就会出岔子。UUID 是 mkfs 时根据时间戳等随机因子生成的,理论上唯一,扩容、重建文件系统之后会变,但正常使用期间不变,是最可靠的引用方式。

查这几个值:

blkid /dev/sdb1 # 输出示例:/dev/sdb1: LABEL="data01" UUID="a1b2c3d4-..." TYPE="ext4" PARTUUID="..." lsblk -f

注意 PARTUUID 和 UUID 是两个东西。UUID 是文件系统的,PARTUUID 是分区的。GPT 下还有 PARTLABEL,就是 parted 里 mkpart 设置的那个名字。这几个概念经常被搞混,简单记:文件系统层面用 UUID,分区层面用 PARTUUID,两个都能用于挂载。

5. 挂载:临时挂载和永久挂载

分区和文件系统都就位了,最后一步是让系统能访问它。临时挂载用 mount 命令,重启就没了;永久挂载写 /etc/fstab,开机自动生效。两部分都有坑。

5.1 mount 命令和常用选项

基础用法:

mkdir -p /data mount /dev/sdb1 /data

挂载点必须先存在,mount 不会帮你建目录。挂载之后可以用df -hT /data确认,看文件系统类型、容量、可用空间是否正确,也可以用mount | grep sdb1看具体挂上了没有、用的是什么选项。

常用的挂载选项:

  • defaults:等价于 rw,suid,dev,exec,auto,nouser,async,绝大多数情况写这个就行
  • noatime:不更新文件的访问时间。默认的 relatime 已经比 atime 省很多了,但读多写多的场景(比如 Web 服务、数据库)用 noatime 还能再省一点写操作
  • nodiratime:只对目录关闭 atime,粒度更细
  • discard:给 SSD 用,实时 TRIM。但这个建议慎用,因为它会让每次删除都触发 TRIM 命令,某些 SSD 上会有性能抖动。更推荐的做法是不挂 discard,改用定时fstrim -av,由 systemd 的 fstrim.timer 每周跑一次
  • noexec:禁止在这个分区上执行二进制文件。/tmp、/var/tmp 这类目录强烈建议加上,能挡掉一部分恶意脚本
  • nosuid:忽略 setuid 位,同样是安全加固
  • nofail:挂载失败不阻塞启动。这个对非系统盘特别重要,尤其是外接盘、网络盘,某次没插上就进不了系统的场景太常见了
  • x-systemd.automount:按需挂载,访问到挂载点才真正挂。适合偶尔用的大容量盘,能省去开机挂载的等待

组合起来一个典型的数据盘配置大概长这样:

mount -o defaults,noatime,nofail /dev/sdb1 /data

挂载选项不是越多越好。noexec和nosuid别乱加到 /home 上,不然用户自己的脚本都跑不了;discard加在机械盘上是无效的;sync这种会严重拖慢性能,除非有极特殊需求,不要用。

5.2 fstab 怎么写才不会把机器搞挂

/etc/fstab 的格式是六个字段:

<设备> <挂载点> <文件系统类型> <挂载选项> <dump> <fsck顺序>

一个标准条目:

UUID=a1b2c3d4-1234-5678-9abc-def012345678 /data ext4 defaults,noatime,nofail 0 2

六个字段的含义:设备(推荐 UUID)、挂载点(必须是绝对路径)、类型(ext4/xfs/swap 等)、选项、dump 备份标志(现在基本都填 0)、开机 fsck 顺序(根分区 1,其他 ext 系填 2,XFS 和 swap 填 0)。

这里有几个必须遵守的规则。

第一,写 UUID 不写设备名。原因前面说过,设备名会变。用 UUID 也可能失效,比如重建了文件系统,但在正常生命周期里它是稳定的。如果实在想用可读的,用 LABEL,但要确保全系统卷标不重复。

第二,非系统盘一律加nofail。这一条怎么强调都不过分。fstab 里任何一个条目挂载失败,systemd 默认会卡在 emergency mode,机器就起不来了。加 nofail 之后,挂不上就跳过,系统正常启动,最多是那个目录空着。如果再配合x-systemd.device-timeout=10s,还能控制等待时间,避免默认的 90 秒卡顿。

第三,改 fstab 之后别急着重启。先跑mount -a验证一遍,没有报错才算过关。mount -a会尝试挂载 fstab 里所有还没挂的条目,写错了会在这里暴露。这一步能救你无数次。

第四,写之前先备份。cp /etc/fstab /etc/fstab.bak.$(date +%F),这条命令我闭着眼睛都能敲。万一把 fstab 改坏了,进 rescue 模式之后挂载根分区为读写,把备份拷回去就好,比现场排错快得多。

5.3 挂载点权限和属主

挂上去不等于能用。新格式化的 ext4 分区挂载之后,根目录的属主是 root:root,权限 755,普通用户写不进去。这时候要么用 root 操作,要么改属主。

chown -R appuser:appgroup /data chmod 750 /data

有个细节值得说:chown 和 chmod 作用在挂载点目录上,实际改的是分区根目录的属性,不是底层目录的。所以先挂载再改权限,顺序不能反。反过来,umount 之后,原来的挂载点目录会恢复成它自己的属性,通常是 root:root 755,这个不影响,重新挂载之后看到的是分区里的属性。

XFS 分区如果要做配额、项目配额,那又是另一套流程,这里不展开。普通场景下 chown 就够了。

另外提一句 SELinux。如果系统开启了 SELinux,改了挂载点之后可能需要执行restorecon -Rv /data重新打标签,否则服务访问会报权限拒绝。看getenforce的返回值,Enforcing 的话就得注意这一层,日志里会出现 AVC denied 的记录。

6. 常见问题与排查实录

这一节是我这些年攒下来的现场记录,按现象、原因、处理方式整理,遇到的时候可以直接对号入座。

6.1 高频问题速查表

现象大概率原因处理方式
mkfs 报 No such file内核未重读分区表partprobe /dev/sdb或partx -u
umount 报 target is busy有进程在用该挂载点lsof +D /data或fuser -mv /data,处理后重试
开机卡在 emergency modefstab 有错误条目救援模式挂根分区,注释掉错误行
超过 2TB 只能认到 2TBMBR 分区表上限改用 GPT 重新分区
写入速度明显偏低分区未 4K 对齐备份数据,按 1MiB 边界重分
存了 65536 个文件后报 No spaceinode 耗尽查df -i,重新 mkfs 加大 inode
挂载后全是问号或只读NTFS 未装 ntfs-3g安装 ntfs-3g,或用-t ntfs-3g
GPT 盘分区表丢失开头的 GPT 头被破坏从磁盘尾部备份恢复
容器报 d_type 不支持XFS 未开 ftype=1备份重格式化,带-n ftype=1

6.2 几个印象深刻的现场

第一个是 fstab 写错导致机器进不了系统。当时给一台机器加数据盘,UUID 复制的时候少了一位,重启直接掉进 emergency mode。现场没有带外管理,只能到机房接显示器。进救援之后的操作是:mount -o remount,rw /把根分区挂成可写,然后vi /etc/fstab把那行注释掉,reboot。前后五分钟,但那个来回跑机房的成本,足够让人记住一辈子。从那以后我改 fstab 的流程固定成三步:先备份,再写入,最后mount -a验证。

第二个是 umount 一直提示 busy。查了半天没找到是谁占着,lsof +D /data输出为空。后来用fuser -mv /data才看到一个 shell 的工作目录还在里面。原因是之前cd /data之后一直没退出。这种属于幽灵气的问题,进程不在挂载点里读写文件,但工作目录挂在上面,内核就认为设备忙。处理方式很简单,把那个 shell 退出来或者换个目录,再 umount 就成功了。如果是服务进程,可以用fuser -km /data强制杀掉占用者,但这条命令杀伤力很大,用之前务必确认。

第三个是磁盘容量"凭空少了一半"。一块 4TB 的盘,分完区之后只认出 2TB。第一反应是分区表类型错了,fdisk -l一看,果然是 dos 类型,MBR 的铁律在起作用。重新用 parted 建 GPT,问题解决。这类问题现在少见了,但如果你接手的是别人配的老设备,还是要留个心眼。

第四个是 inode 耗尽。一个目录下存了几百万个小文件,df -h显示还有空间,但写入报 "No space left on device"。df -i一看,inode 用了 100%。原因是 mkfs 时用了默认的 inode 密度,算下来 inode 总数不够。补救方式只能是备份数据、重新 mkfs 并指定更大的 inode 数量(mkfs.ext4 -i 4096,即每 4KiB 一个 inode),再恢复数据。这也说明了 mkfs 之前想清楚使用场景有多重要。

第五个是 GPT 分区表损坏。某次做测试的时候用 dd 往盘上写了一大块数据,把开头的 GPT 头覆盖了,parted print直接报 "unrecognised disk label"。因为分区表本身还在磁盘尾部有备份,用sgdisk -e /dev/sdb重建备份结构,再partprobe刷新,分区就回来了。这个经历让我对 GPT 的好感直线上升——MBR 遇到同样的情况,基本就只能上 testdisk 扫描碰运气了。

6.3 几个不写在文档里的小技巧

分完区之后立刻用parted -s /dev/sdb unit s print看一眼起始扇区,比事后性能测试发现问题再返工要省太多事。

写 fstab 之前,先把 UUID 用blkid查出来,存到一个文本文件里,粘贴的时候不容易错位。我习惯把blkid的完整输出重定向到 /root/disk-info.txt,出问题的时候这文件就是救命稻草。

给每个分区设一个有意义的分区名称(GPT 的 PARTLABEL)和卷标,lsblk -o NAME,SIZE,PARTLABEL,LABEL,MOUNTPOINT一眼就能对应上业务,盘多的时候能省下大量"这是哪块盘"的确认工作。

生产环境的机器,如果不是系统盘,fstab 一律加nofail;如果是系统盘,改之前一定留好现场救援的通道,哪怕只是确认一下带外管理能用。

mount -a验证通过之后,可以再reboot一次确认。虽然多花几分钟,但比在故障夜里排查值得多。

6.4 扩容场景的延伸做法

盘用满了要扩,是迟早的事。ext4 和 XFS 都支持在线扩容,前提是底层块设备已经变大。

如果是虚拟机,先在控制台上把虚拟磁盘扩容,然后在系统里echo 1 > /sys/class/block/sdb/device/rescan让内核重新读取磁盘容量,lsblk能看到盘变大了。接着parted /dev/sdb resizepart 1 100%把分区扩到新边界,partprobe刷新。最后resize2fs /dev/sdb1(ext4)或xfs_growfs /data(XFS,注意参数是挂载点不是设备)。整套流程走下来不需要卸载,只要文件系统在挂载状态即可。

这一套里最容易出问题的是 resizepart 之后分区表刷新那一步。有些内核版本对在线扩分区的支持不好,会报分区忙。可以试试partx -u /dev/sdb --nr 1,或者干脆选个业务低峰期做,做完重启一次。具体的兼容性跟内核版本和存储栈都有关,没有通用解法,只能实测。

最后再分享一个小技巧。如果你手上有大量磁盘需要做同样的分区和格式化,别一条条敲,写成脚本用 sgdisk 批量处理,做完之后统一lsblk -f核对一遍。sgdisk 有个--backup和--load-backup的功能,可以把一块盘的分区表结构导出再套到其他盘上,批量初始化的时候非常省事,前提是各盘容量一致。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询