磁盘管理与文件系统
本文合并自:01-磁盘管理与文件系统.md、07-磁盘与文件系统管理.md、1-3-Linux磁盘与LVM管理.md、02-LVM逻辑卷命令速查.md
本文系统讲解 Linux 下磁盘从接入到可用的完整流程:存储基础与设备识别、分区表(MBR/GPT)与分区工具(
fdisk/parted/gdisk)、文件系统的创建与挂载、/etc/fstab开机自动挂载、swap 交换分区、LVM 逻辑卷管理、磁盘配额、LUKS 加密、软件 RAID、磁盘性能与排错。文中同时保留 CentOS 6 与 CentOS 7 两代系统的不同做法并标注适用版本。
本章要点
- 理解块设备的标准操作流程:分区 → 格式化(创建文件系统)→ 挂载。
- 掌握 Linux 设备文件命名规则(
hd*/sd*/vd*/nvme*),理解 MBR 与 GPT 两种分区表的区别。 - 掌握
fdisk交互分区(n/d/p/t/l/w/q)、分区类型修改,以及保存后用partprobe/partx生成设备文件。 - 掌握 GPT 与大于 2TB 磁盘必须使用的
parted/gdisk。 - 掌握分区表的备份与恢复方法。
- 掌握
mkfs/mkfs.ext4/mkfs.xfs/mkfs.vfat/mkfs.ntfs/mkswap等格式化命令。 - 掌握
mount/umount,包括挂载选项(ro/rw/remount/bind/loop/noauto/noexec等)、NFS/ISO/NTFS 挂载、强制卸载与占用排查。 - 掌握
swapon/swapoff/free -m管理交换分区。 - 掌握
/etc/fstab六个字段含义、使用 UUID 挂载、mount -a、blkid。 - 掌握文件系统检查修复(
fsck/e2fsck/xfs_repair)与在线调整(resize2fs/xfs_growfs/tune2fs)。 - 掌握 LVM 三层结构(PV→VG→LV)及
pvcreate/vgcreate/lvcreate/lvextend -r等命令,理解扩容与缩减流程、快照原理。 - 掌握磁盘配额 quota / xfs_quota 与 LUKS 加密分区的完整实验步骤。
- 掌握
mdadm软件 RAID 的创建、监控、故障模拟与恢复。 - 能够独立排查 inode 耗尽、磁盘满、fstab 写错无法启动、设备忙无法卸载等常见故障。
一、存储基础与设备识别
1.1 磁盘创建流程总览
一块磁盘在 Linux 中从接入到可被使用,通常需要经历以下步骤:
RAID(可选)
→ 远程存储访问 iSCSI / GNBD(可选)
→ 分区
→ LVM(可选)
→ 格式化(创建文件系统)
→ 挂载
→ 分布式文件系统(可选)
其中最核心、必做的三个环节是 分区、格式化、挂载:
分区 → 格式化(创建文件系统) → 挂载
磁盘管理的对象是存储设备(块设备),包括硬盘、移动硬盘、U 盘、光盘、软盘、磁盘阵列等。
警告:编辑分区表是危险操作,操作不当可能导致分区无法访问、数据"丢失"(数据其实仍在磁盘上并未真正被擦除,但访问会非常困难)。安全做法是先将分区表备份到其他存储设备,再进行修改,以便随时恢复。
常用的分区管理工具:
| 工具 | 支持的分区表 | 适用版本/场景 |
|---|---|---|
fdisk |
MBR(MSDOS);CentOS 7 起的新版本也能操作 GPT,但功能不完整 | CentOS 6 的默认分区工具;≤2TB 磁盘 |
parted |
MSDOS(MBR)与 GPT 都支持 | GPT 分区必须使用它;>2TB 磁盘必须使用它 |
gdisk |
专用于 GPT | UEFI + GPT 环境,交互方式与 fdisk 类似 |
1.2 机械硬盘的物理结构
机械硬盘(HDD)由若干盘片(Platter)叠放构成,每个盘片的上下两面都有一个磁头(Head)负责读写:
盘片 Platter ── 磁性介质载体,双面可用,多片叠放在同一主轴上
磁头 Head ── 每个盘面一个磁头,悬浮在盘面上读写数据
磁道 Track ── 磁头在盘面上划出的同心圆
柱面 Cylinder── 所有盘面上半径相同的磁道,纵向组合成的"圆柱"
扇区 Sector ── 磁道被等分成的小段,是磁盘读写的最小物理单位
关键概念与换算:
- 扇区(Sector):传统扇区大小为 512 字节,是磁盘 I/O 的最小单位。
- 4K 扇区(Advanced Format):现代大容量硬盘的物理扇区为 4096 字节(4K),为兼容旧系统对外仍模拟报告 512 字节逻辑扇区(称为 512e)。若分区起始位置未按 4K 对齐(4K 对齐 = 起始扇区号能被 8 整除),会造成"读写放大",性能显著下降。
fdisk -l输出中的Sector size (logical/physical): 512 bytes / 4096 bytes就是逻辑扇区/物理扇区大小。- 柱面(Cylinder):旧版 fdisk 默认以柱面为分区单位(1 柱面 = 磁头数 × 每磁道扇区数 × 512 字节,常见为 255 × 63 × 512 = 8225280 字节 ≈ 8MB)。CentOS 7 的 fdisk 默认改用扇区为单位,并自动做 1MB 对齐。
- MBR 位于 0 号磁头、0 号磁道、1 号扇区(即整块盘的第一个扇区)。
1.3 SSD 与机械硬盘的差异
| 对比项 | 机械硬盘 HDD | 固态硬盘 SSD |
|---|---|---|
| 存储介质 | 磁性盘片 + 磁头机械寻道 | NAND 闪存芯片,无机械部件 |
| 随机 IOPS | 低(受寻道时间限制,约 100~200) | 极高(数千至数十万) |
| 顺序吞吐 | 100~200 MB/s | SATA SSD 约 500MB/s;NVMe 可达数 GB/s |
| 访问延迟 | 毫秒级(含寻道 + 旋转延迟) | 微秒级 |
| 抗震/噪音/功耗 | 怕震动、有噪音、功耗高 | 抗震、静音、功耗低 |
| 寿命特征 | 机械磨损,坏道 | 闪存有擦写次数上限(P/E 寿命) |
| 特殊维护 | 需要碎片整理(Linux 下基本不需要) | 需要 TRIM(fstrim -av 或挂载选项 discard);不要做碎片整理 |
SSD 使用建议:
- 对 SSD 上的文件系统使用
noatime或relatime挂载选项,减少无意义写入。 - 使用
fstrim定时任务(CentOS 7 提供fstrim.timer)而不是挂载选项discard,后者会带来即时写入延迟。 - 分区务必 4K 对齐(现代
fdisk/parted默认已对齐到 1MiB)。
1.4 接口类型与设备命名规则
| 接口 | 说明 | 典型设备文件 |
|---|---|---|
| IDE(ATA/PATA) | 早期并行接口,已淘汰 | /dev/hda、/dev/hdb |
| SATA | 主流桌面/服务器串行接口 | /dev/sda、/dev/sdb |
| SCSI | 传统企业级并行接口 | /dev/sda … |
| SAS | 串行 SCSI,企业级主流 | /dev/sda … |
| USB(移动硬盘、U 盘) | 内核按 SCSI 子系统处理 | /dev/sdb、/dev/sdc … |
| NVMe(PCIe SSD) | 高速固态盘专用协议 | /dev/nvme0n1 |
| 虚拟机 virtio 磁盘 | KVM/云主机半虚拟化磁盘 | /dev/vda、/dev/vdb |
| 光驱 | 光盘设备 | /dev/sr0、/dev/cdrom(软链接) |
| 软驱 | 已淘汰 | /dev/fd0 |
| 内存盘 | RAM disk | /dev/ram0 |
设备名各位置的含义(以 /dev/sda1 为例):
- 第一位
s/h:接口类型。SATA/SCSI/SAS/USB/移动硬盘为s,IDE 为h; d:表示存储设备(disk);- 第三位
a/b/c…:同类型硬盘的序号,a为第一块,b为第二块,e为第五块; - 末尾数字:该硬盘上的分区编号。
常见设备文件对照:
| 设备文件 | 含义 |
|---|---|
/dev/sda |
第一块 SATA/SCSI/SAS/USB 硬盘 |
/dev/sdb |
第二块 SATA/SCSI/SAS/USB 硬盘 |
/dev/hda |
第一块 IDE 硬盘 |
/dev/sda1 |
第一块 SATA 硬盘的第一个分区 |
/dev/sda2 |
第一块 SATA 硬盘的第二个分区 |
/dev/vda1 |
虚拟机 virtio 第一块盘的第一个分区 |
/dev/nvme0n1p1 |
第一块 NVMe 控制器、第一个命名空间、第一个分区 |
NVMe 命名规则拆解:/dev/nvme0n1p2 = nvme + 控制器编号 0 + n 命名空间 1 + p 分区 2。注意 NVMe 与 mmcblk(eMMC/SD 卡,如 /dev/mmcblk0p1)的分区编号前带字母 p,而 sd/vd 系列直接接数字。
1.5 分区编号规则(MBR)
MBR 分区表下的编号有固定规则,必须记住:
/dev/sda1 ─┐
/dev/sda2 ├─ 主分区或扩展分区,编号固定占用 1~4
/dev/sda3 │
/dev/sda4 ─┘
/dev/sda5 ─┐
/dev/sda6 ├─ 逻辑分区,编号一律从 5 开始,依次递增,无上限
/dev/sda7 ─┘
- 主分区(Primary)与扩展分区(Extended)共享 1~4 这四个编号。
- 逻辑分区(Logical)的编号一定从 5 开始,即使只有 1 个主分区 + 1 个扩展分区,第一个逻辑分区依然是
/dev/sda5,不会是/dev/sda3。 - 删除中间的逻辑分区会导致后面的逻辑分区编号前移(例如删掉 sda6,原 sda7 变成 sda6),这也是
/etc/fstab中不应写死设备名的重要原因。 - GPT 分区表没有主/扩展/逻辑之分,编号从 1 开始连续排列。
1.6 设备文件与 /dev 目录
Linux 遵循"一切皆文件"的设计,所有硬件都通过 /dev 下的设备文件访问。设备文件分两类:
| 类型 | 标识 | 特点 | 举例 |
|---|---|---|---|
| 块设备(block) | ls -l 首字符为 b |
按块随机读写、带缓冲 | /dev/sda、/dev/sdb1、/dev/md0、/dev/mapper/vg-lv |
| 字符设备(character) | ls -l 首字符为 c |
按字节流顺序读写、无缓冲 | /dev/tty、/dev/null、/dev/zero、/dev/random |
ls -l /dev/sda /dev/tty0
# brw-rw---- 1 root disk 8, 0 ... /dev/sda <- b 为块设备,8,0 是主/次设备号
# crw--w---- 1 root tty 4, 0 ... /dev/tty0 <- c 为字符设备
/dev 下常见设备文件:
| 设备文件 | 对应硬件/用途 |
|---|---|
/dev/pts |
伪终端(远程登录终端) |
/dev/tty |
本地终端,可通过 w、who am i、tty 查看当前终端 |
/dev/ram |
内存盘 |
/dev/input |
输入设备(鼠标、键盘等) |
/dev/sda |
第一块 SCSI/SATA 硬盘 |
/dev/sda1 |
第一块硬盘的第一个分区 |
/dev/stdin |
标准输入设备(键盘),文件描述符 0 |
/dev/stdout |
标准输出设备(显示器),文件描述符 1 |
/dev/stderr |
标准错误输出设备(显示器),文件描述符 2 |
/dev/null |
黑洞设备,写入的数据被丢弃 |
/dev/zero |
零设备,读取时无限输出 \0,常配合 dd 造文件 |
/dev/urandom |
随机数设备,用于生成密钥、随机填充 |
/dev/loop0 |
回环设备,把普通文件当块设备使用 |
/dev/mapper/* |
device-mapper 设备(LVM 逻辑卷、LUKS 加密卷) |
1.7 查看磁盘与分区信息的命令
lsblk:树形查看块设备(最常用)
lsblk # 以树型结构查看所有硬盘与分区
lsblk -f # 附带文件系统类型、LABEL、UUID、挂载点
lsblk -o NAME,SIZE,TYPE,FSTYPE,MOUNTPOINT,UUID # 自定义输出列
lsblk -d # 只显示磁盘本身,不显示分区
lsblk -p # 显示完整设备路径(/dev/sda 而非 sda)
lsblk -b # 以字节为单位显示容量
lsblk -m # 显示权限与属主信息
lsblk /dev/sdb # 只看指定磁盘
典型输出:
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
sda 8:0 0 20G 0 disk
├─sda1 8:1 0 500M 0 part /boot
└─sda2 8:2 0 19.5G 0 part
├─centos-root 253:0 0 17.5G 0 lvm /
└─centos-swap 253:1 0 2G 0 lvm [SWAP]
sdb 8:16 0 5G 0 disk
sr0 11:0 1 1024M 0 rom
TYPE 列区分 disk(整盘)、part(分区)、lvm(逻辑卷)、raid5(阵列)、rom(光驱)、crypt(加密卷)。
fdisk -l:查看分区表
fdisk -l # 列出系统中所有块设备的分区表
fdisk -l /dev/sda # 仅列出 /dev/sda 的分区表
sudo fdisk -l # 非 root 用户需要 sudo
CentOS 6 风格示例输出:
Disk /dev/sda: 500.1 GB, 500107862016 bytes
255 heads, 63 sectors/track, 60801 cylinders
Units = cylinders of 16065 * 512 = 8225280 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0x0003f808
Device Boot Start End Blocks Id System
/dev/sda1 * 1 33 262144 83 Linux
Partition 1 does not end on cylinder boundary.
/dev/sda2 33 7682 61440000 8e Linux LVM
/dev/sda3 7682 7747 524288 82 Linux swap / Solaris
输出解读:Boot 列的 * 表示引导分区;Id 列是分区类型 ID(83 Linux、8e Linux LVM、82 swap);Blocks 以 1KB 为单位。
CentOS 6 与 7 差异:CentOS 6 的
fdisk默认单位是柱面(cylinders),CentOS 7 默认单位是扇区(sectors)且自动 1MiB 对齐,输出中不再有Units = cylinders of ...一行。
parted -l:查看分区表(支持 GPT)
parted -l # 列出所有磁盘分区信息,能正确显示 GPT
parted /dev/sdb print # 查看指定磁盘
parted -s /dev/sdb print # -s 脚本模式,不提示交互
对 GPT 磁盘必须用 parted -l 或 gdisk -l;用 CentOS 6 的 fdisk -l 查看 GPT 磁盘会给出 "WARNING: GPT (GUID Partition Table) detected on '/dev/sdb'! The util fdisk doesn't support GPT" 的告警。
blkid:查看 UUID、LABEL 与文件系统类型
blkid # 查看所有块设备的 UUID/LABEL/TYPE
blkid /dev/sdc1 # 查看指定分区
blkid -s UUID -o value /dev/sdb1 # 只输出 UUID 的值,便于脚本使用
blkid -L mydata # 按卷标反查设备
blkid -U <uuid> # 按 UUID 反查设备
/dev/sda1: LABEL="/vm" UUID="174c6b5a-0de6-469d-bc07-b38d4bfd3479" TYPE="ext4"
df:查看已挂载文件系统的空间使用
df -h # 以易读方式(K/M/G)显示各分区挂载点及使用情况
df -h /data # 查看某目录所在文件系统
df -T # 额外显示文件系统类型
df -i # 查看 inode 使用情况(排查"磁盘没满却报空间不足")
df -hT --total # 显示合计行
df -x tmpfs -x devtmpfs # 排除指定类型的文件系统
df -a # 显示包括伪文件系统在内的全部条目
df -k / -m # 以 KB / MB 为单位
df -h 各列:Filesystem(设备)、Size(总容量)、Used(已用)、Avail(可用)、Use%(使用率)、Mounted on(挂载点)。
注意
Size ≠ Used + Avail,因为 ext 文件系统默认为 root 预留 5% 的保留块(可用tune2fs -m调整)。
du:统计目录/文件占用的空间
du -sh /var # 统计 /var 总大小(-s 汇总,-h 易读)
du -sh * # 统计当前目录下每个条目的大小
du -h --max-depth=1 /var # 只看一层子目录(等价 du -h -d 1)
du -sh --exclude=*.log /var/log # 排除某类文件
du -a /etc | sort -rn | head -20 # 找出最大的文件(不带 -h 才能正确排序)
du -sh * | sort -rh | head -10 # 按大小倒序取前 10(GNU sort 支持 -h)
du --apparent-size -sh file # 显示"表观大小",用于观察稀疏文件
du -x -sh / # 不跨文件系统统计
df 与 du 的区别:df 从超级块读取统计值(快、看整个文件系统),du 遍历目录逐个累加(慢、看某个目录)。两者结果不一致的典型原因是"文件已删除但仍被进程占用"(见排错清单)。
其它设备信息查看命令
cat /proc/partitions # 内核当前识别到的所有分区(主设备号、次设备号、块数、名称)
cat /proc/scsi/scsi # 查看 SCSI 设备信息
lsscsi # 以易读格式列出 SCSI/SATA 设备(需 yum install lsscsi)
lsscsi -s # 附带容量显示
lsblk -S # CentOS 7 起可用,等效查看 SCSI 设备
cat /sys/block/sda/size # 以 512 字节扇区数表示的磁盘容量
cat /sys/block/sda/queue/rotational # 1=机械盘,0=SSD
hdparm 查看/设置硬盘参数:
hdparm -I /dev/sda # 查看硬盘详细参数(型号、序列号、支持特性)
hdparm -i /dev/sda # 查看内核识别到的简要参数
hdparm -tT /dev/sda # 测试缓存读与磁盘读性能
hdparm -W /dev/sda # 查看写缓存是否开启
hdparm -W 1 /dev/sda # 开启写缓存(1 开 / 0 关)
hdparm -C /dev/sda # 查看电源状态(active/standby)
hdparm -y /dev/sda # 立即让硬盘进入待机
smartctl 查看硬盘健康状态(需 yum install smartmontools -y):
smartctl -i /dev/sda # 查看设备基本信息与 SMART 是否启用
smartctl -s on /dev/sda # 启用 SMART 支持
smartctl -H /dev/sda # 查看健康状况总体结论(PASSED / FAILED!)
smartctl -A /dev/sda # 查看所有 SMART 属性明细
smartctl -a /dev/sda # 查看全部信息(信息 + 属性 + 日志)
smartctl -l selftest /dev/sda # 查看自检日志
smartctl -t short /dev/sda # 启动短自检(约 2 分钟)
smartctl -t long /dev/sda # 启动长自检(可能数小时)
smartctl -d sat -a /dev/sda # USB/SAT 转接盘需要指定设备类型
重点关注的 SMART 属性(数值持续增长即预示硬盘即将故障):
| ID | 属性名 | 含义 |
|---|---|---|
| 5 | Reallocated_Sector_Ct | 重映射扇区数,非 0 且增长即需换盘 |
| 197 | Current_Pending_Sector | 等待重映射的可疑扇区 |
| 198 | Offline_Uncorrectable | 无法纠正的扇区数 |
| 187 | Reported_Uncorrect | 报告的不可纠正错误 |
| 194 | Temperature_Celsius | 温度 |
| 9 | Power_On_Hours | 通电时长 |
| 241 | Total_LBAs_Written | 累计写入量(判断 SSD 寿命) |
/dev/disk 下的持久化设备名
设备名 /dev/sdb 可能因磁盘插拔顺序变化而改变,/dev/disk/ 下提供了稳定的别名(都是指向 ../../sdXn 的软链接):
ls -l /dev/disk/by-uuid/ # 按文件系统 UUID 索引,fstab 首选
ls -l /dev/disk/by-label/ # 按文件系统卷标索引
ls -l /dev/disk/by-id/ # 按硬件序列号/WWN 索引,物理定位磁盘最可靠
ls -l /dev/disk/by-path/ # 按物理连接路径(PCI 总线、SCSI 通道)索引
ls -l /dev/disk/by-partuuid/ # 按 GPT 分区 UUID 索引
lrwxrwxrwx 1 root root 10 Aug 22 10:11 174c6b5a-0de6-... -> ../../sda1
用途区分:by-uuid / by-label 适合写进 /etc/fstab(跟着文件系统走,重新格式化会变);by-id / by-path 适合识别"物理上是哪块盘、插在哪个槽位",换盘、排查故障盘时非常有用。
1.8 虚拟机添加新硬盘的完整步骤
以 VMware Workstation / ESXi 为例:
-
关机或热添加:虚拟机 → 设置 → 添加 → 硬盘 → SCSI(推荐)→ 创建新虚拟磁盘 → 指定容量(如 5GB)→ 完成。
-
进入系统确认:
lsblk # 看是否出现 sdb fdisk -l | grep -i disk ls /dev/sd* -
若关机添加:开机后即可看到新盘
/dev/sdb。 -
若热添加(不重启):需要让内核重新扫描 SCSI 总线,见下节。
-
后续流程:分区 → 格式化 → 挂载 → 写入
/etc/fstab(或者直接把整盘做成 LVM 的 PV)。
1.9 在线扫描识别新磁盘(不重启)
# 1. 先找出系统中有哪些 SCSI host
ls /sys/class/scsi_host/
# host0 host1 host2
# 2. 对每个 host 触发重新扫描(三个减号分别代表 channel、target、lun 的通配)
echo '- - -' > /sys/class/scsi_host/host0/scan
echo '- - -' > /sys/class/scsi_host/host1/scan
echo '- - -' > /sys/class/scsi_host/host2/scan
# 一次性对所有 host 扫描
for h in /sys/class/scsi_host/host*/scan; do echo '- - -' > $h; done
# 3. 确认新盘已被识别
lsblk
fdisk -l
dmesg | tail -20 # 观察内核日志中新设备的识别过程
已有磁盘在虚拟化平台上被扩容后,让内核重新读取新容量:
echo 1 > /sys/class/block/sdb/device/rescan # 重新读取该盘容量
# 或
echo 1 > /sys/block/sdb/device/rescan
lsblk # 确认 SIZE 已变大
删除一块已拔掉的磁盘(避免残留 I/O 错误):
echo 1 > /sys/block/sdb/device/delete
使用 rescan-scsi-bus.sh(yum install sg3_utils -y)可以更方便地完成扫描:
rescan-scsi-bus.sh # 扫描新增设备
rescan-scsi-bus.sh -r # 同时移除已消失的设备
rescan-scsi-bus.sh -s # 扫描容量变化(resize)
二、分区管理:MBR 与 GPT
2.1 引导方式与分区表的对应关系
系统引导方式与分区表、分区工具是一一对应的:
BIOS → MBR → fdisk(MBR 分区管理工具)
UEFI → GPT → gdisk / parted(GPT 分区管理工具)
- MBR 是长期以来最流行的分区模式,稳定性高、兼容性最好;
- GPT 是未来主流,支持 2TB 以上硬盘,而 MBR 不支持 2TB 以上的单盘容量。
2.2 MBR 分区表结构
硬盘的第一个逻辑块(扇区)用于存储 MBR(Master Boot Record,主引导记录)信息,共 512 字节:
MBR (512 字节)
├── 0 ~ 445 引导代码 Boot Code 446 字节
├── 446 ~ 509 分区表 Partition Table 64 字节(4 项 × 16 字节)
└── 510 ~ 511 结束标志 0x55AA 2 字节
简化记法:
MBR(512) = 启动代码(446) + 分区表(64) + 结束标志(2)
- 分区表共 64 字节,每个分区信息占 16 字节,因此最多只能记录 4 个分区,这 4 个分区称为主分区(Primary Partition)。
- 每个 16 字节分区项中用 4 字节记录分区起始 LBA、4 字节记录分区扇区总数,因此单分区最大 2^32 × 512 字节 = 2TB,这就是 MBR 的 2TB 容量上限来源。
如需超过 4 个分区,就要用扩展分区机制:
- 扩展分区(Extended Partition):占用一个主分区位(即 4 个位置中的一个),本身不能直接存放数据、不能格式化,只用于"存放逻辑分区的信息",相当于一个容器。通常把剩余空间全部分配给它。
- 逻辑分区(Logical Partition):在扩展分区内部继续划分出来的分区,可格式化使用,数量理论上不受限制(实际受内核设备数限制)。逻辑分区的信息以链表形式串在扩展分区内部(EBR)。
- 常见组合:4 个主分区,或 3 个主分区 + 1 个扩展分区(内含 N 个逻辑分区)。
- 逻辑分区编号一律从 5 开始。
2.3 GPT 分区表结构
GPT(GUID Partition Table,全局唯一标识分区表)是 UEFI 规范的一部分:
LBA 0 Protective MBR(保护性 MBR,供旧工具识别,防止误操作)
LBA 1 GPT 头(Primary GPT Header):签名、版本、分区项数量、CRC 校验
LBA 2 ~ 33 分区项数组(默认 128 项,每项 128 字节)
... 实际数据分区区域
倒数第 33 ~ 2 分区项数组备份
最后一个 LBA GPT 头备份(Secondary GPT Header)
GPT 的关键特性:
- 备份分区表:分区表在磁盘头部和尾部各存一份,且带 CRC32 校验,头部损坏可从尾部恢复,可靠性远高于 MBR。
- GUID:磁盘本身有一个 Disk GUID,每个分区也有独立的 Partition GUID(PARTUUID)和分区类型 GUID(如 Linux 文件系统类型 GUID 为
0FC63DAF-8483-4772-8E79-3D69D8477DE4),彻底避免了 MBR 只有 1 字节类型 ID 的冲突问题。 - 突破 4 分区限制:默认支持 128 个分区,且没有主分区/扩展分区/逻辑分区的概念,所有分区地位平等,编号从 1 连续递增。
- 突破 2TB 限制:使用 64 位 LBA 寻址,理论支持 8 ZiB(约 90 亿 TB),实际使用中远超需求。大于 2TB 的磁盘必须使用 GPT。
- 与 UEFI 的关系:UEFI 固件从 GPT 上的 ESP 分区(EFI System Partition,FAT32 格式,类型 GUID
C12A7328-...,通常挂载在/boot/efi)中读取.efi引导程序启动系统,不再依赖 MBR 里的 446 字节引导代码。若要在 GPT 磁盘上用传统 BIOS 引导,则需要额外创建一个 1MB 的 BIOS Boot Partition(类型ef02)供 GRUB 存放 core.img。
2.4 MBR 与 GPT 对比表
| 特性 | MBR | GPT |
|---|---|---|
| 全称 | Master Boot Record | GUID Partition Table |
| 适用启动模式 | BIOS(Legacy) | UEFI(也可配合 BIOS + BIOS Boot 分区) |
| 主要管理工具 | fdisk |
gdisk、parted |
| 最大支持单盘/分区容量 | 2 TB 以内 | 2 TB 以上(理论 8 ZiB) |
| 分区数量 | 最多 4 个主分区,或 3 主分区 + 1 扩展分区(内含多个逻辑分区) | 默认 128 个,无主/扩展/逻辑之分 |
| 分区表位置 | 仅磁盘第一个扇区,无备份 | 磁盘头尾各一份,带 CRC 校验 |
| 分区类型标识 | 1 字节 ID(83/8e/82/7…) |
16 字节 GUID |
| 稳定性/兼容性 | 稳定、兼容老系统(含 Windows XP 32 位) | 主流趋势,老系统可能不识别 |
| CentOS 支持 | CentOS 5/6/7 均完全支持 | CentOS 6 需用 parted;CentOS 7 起 fdisk 也部分支持 |
选择建议:
- 单盘容量 ≤ 2TB、需要兼容老系统或老 BIOS → 用 MBR。
- 单盘容量 > 2TB、使用 UEFI 引导、需要 4 个以上分区 → 必须用 GPT。
- 服务器新装系统、云主机数据盘 → 一律建议 GPT。
- 用作 LVM PV 的数据盘,也可以不分区直接把整盘做 PV(
pvcreate /dev/sdb),这样连分区表的限制都绕开了。
2.5 常见分区类型 ID(MBR,fdisk 中用 t 设置)
| ID | 类型 | 用途 |
|---|---|---|
83 |
Linux | 普通 Linux 文件系统分区(ext2/3/4、xfs),默认值 |
82 |
Linux swap / Solaris | 交换分区 |
8e |
Linux LVM | 作为 LVM 物理卷使用的分区 |
fd |
Linux raid autodetect | 作为软件 RAID 成员的分区 |
7 |
HPFS/NTFS/exFAT | Windows NTFS/exFAT 分区 |
b |
W95 FAT32 | FAT32 分区 |
c |
W95 FAT32 (LBA) | FAT32(LBA 模式) |
ef |
EFI System | EFI 系统分区(ESP) |
ee |
GPT | GPT 保护分区标记 |
5 |
Extended | 扩展分区 |
f |
W95 Ext'd (LBA) | 扩展分区(LBA 模式) |
在 fdisk 中输入 l(或在 t 命令后输入 L)可以列出全部已知类型。
注意:分区类型 ID 只是"声明用途"的元数据标记,它不等于实际的文件系统。把分区 ID 改成
8e并不会自动把它变成 PV,仍需执行pvcreate;反之即使 ID 是83,也一样可以pvcreate。设置正确的 ID 是良好习惯,便于其他工具和人类正确识别用途。
2.6 分区方案示例
MBR 个人多系统分区方案
| 分区 | 大小 | 用途 |
|---|---|---|
| 第一个主分区 | 50 GB | Windows C 盘 |
| 第二个主分区 | 50 GB | 黑苹果系统分区 |
| 第三个主分区 | 4 GB | /boot 分区 |
| 第四个主分区 | 剩余空间 | 扩展分区 |
| └ 第一个逻辑分区 | 4 GB | Swap 交换分区 |
| └ 第二个逻辑分区 | 50 GB | Linux 的 /home 个人数据 |
| └ 第三个逻辑分区 | 30 GB | Ubuntu 根分区 |
| └ 第四个逻辑分区 | 20 GB | CentOS 根分区 |
| └ 第五个逻辑分区 | 20 GB | 测试 Linux 根分区 |
| └ 最后一个逻辑分区 | 剩余空间 | Windows D 盘 |
GPT 个人分区方案(不再受 4 主分区限制)
| 序号 | 大小 | 用途 |
|---|---|---|
| 1 | 4 GB | ESP 分区(boot 分区) |
| 2 | 1 MB | BIOS 兼容分区(BIOS Boot Partition) |
| 3 | 50 GB | Windows 7 C 盘 |
| 4 | 4 GB | Swap 交换分区 |
| 5 | 50 GB | Linux /home |
| 6 | 30 GB | Ubuntu 根分区 |
| 7 | 20 GB | CentOS 根分区 |
| 8 | 20 GB | 测试 Linux 根分区 |
| 最后 | 剩余空间 | Windows D 盘 |
GPT + LVM 个人分区方案
结合 LVM 时,可以将若干系统分区合并为一个大的 LVM 分区(如 100GB),再在其中按需划分逻辑卷:
| 分区 | 大小 | 用途 |
|---|---|---|
| 分区 1 | 4 GB | ESP(boot)分区 |
| 分区 2 | 1 MB | BIOS 兼容分区 |
| 分区 3 | 50 GB | Windows C 盘 |
| 分区 4 | 100 GB | LVM 物理卷(内部再划分 root/home/swap/各测试系统) |
实验用 MBR 分区方案(对应下文实操与练习)
| 设备 | 大小 | 挂载点/用途 | 文件系统 ID |
|---|---|---|---|
/dev/sda1 |
30 GB | /(CentOS) |
83(Linux) |
/dev/sda2 |
30 GB | Windows C 盘 | 7(NTFS) |
/dev/sda3 |
4 GB | /boot |
83(Linux) |
/dev/sda4 |
剩余 | 扩展分区 | — |
/dev/sda5 |
4 GB | Swap | 82(Swap) |
/dev/sda6 |
50 GB | /home |
83(Linux) |
/dev/sda7 |
30 GB | Ubuntu 根分区 | 83(Linux) |
/dev/sda8 |
20 GB | Mint 根分区 | 83(Linux) |
| 最后逻辑分区 | 剩余 | Windows D 盘 | 7(NTFS) |
2.7 fdisk 完整实操
进入交互分区界面
fdisk /dev/sdb # 对指定磁盘进行分区管理
sudo fdisk /dev/sda # 普通用户需 sudo
进入交互命令提示符:
Command (m for help):
若不带磁盘参数直接执行
fdisk,只会打印用法帮助;务必明确指定要操作的磁盘,操作错盘会毁掉系统。
交互命令完整一览
在交互界面中输入 m 可查看所有可用命令:
| 命令 | 英文原文 | 含义 |
|---|---|---|
a |
toggle a bootable flag | 切换分区的引导标志,将分区设为引导分区(*) |
b |
edit bsd disklabel | 编辑 BSD 磁盘标签 |
c |
toggle the dos compatibility flag | 切换 DOS 兼容标志 |
d |
delete a partition | 删除一个分区 |
l |
list known partition types | 列出所有已知的分区类型 ID |
m |
print this menu | 打印帮助菜单 |
n |
add a new partition | 新建一个分区 |
o |
create a new empty DOS partition table | 创建一个新的空 DOS(MBR) 分区表 |
p |
print the partition table | 打印当前分区表 |
q |
quit without saving changes | 不保存直接退出(放弃所有修改) |
s |
create a new empty Sun disklabel | 创建空的 Sun 磁盘标签 |
t |
change a partition's system id | 修改分区类型 ID |
u |
change display/entry units | 更改显示/输入单位(柱面/扇区切换) |
v |
verify the partition table | 校验分区表 |
w |
write table to disk and exit | 保存分区表并退出 |
x |
extra functionality (experts only) | 专家扩展功能 |
g |
create a new empty GPT partition table | 创建 GPT 分区表(仅 CentOS 7 及以上) |
G |
create an IRIX (SGI) partition table | 创建 IRIX 分区表(CentOS 7) |
F |
list free unpartitioned space | 列出未分配空间(CentOS 7) |
i |
print information about a partition | 打印某分区详细信息(CentOS 7) |
日常使用中重点掌握 a、d、l、n、p、q、t、w 即可。
创建主分区完整演练
输入 n 开始新建分区:
Command (m for help): n
Command action
e extended
p primary partition (1-4)
e:创建扩展分区;p:创建主分区(MBR 一块磁盘最多 4 个主分区)。- 注意:只有还有主分区位(1~4 未用满)时才会出现这个询问;主分区位用满且已有扩展分区时,
n会直接创建逻辑分区。
选择 p 创建主分区:
p
Partition number (1-4): 1
First cylinder (1-652, default 1):
Using default value 1
Last cylinder, +cylinders or +size{K,M,G} (1-652, default 652): +1G
操作说明(CentOS 6 以柱面为单位;CentOS 7 提示词为 First sector/Last sector,以扇区为单位):
- Partition number:选择分区编号(主分区 1–4),一般直接回车用默认值;
- First cylinder / First sector:起始位置,直接回车使用默认值(紧接上一个分区之后,自动对齐);
- Last cylinder / +size:指定分区大小。推荐使用
+size{K,M,G}形式,例如+1G、+500M、+10240K、+30G;直接回车则把剩余空间全部用完。
创建扩展分区与逻辑分区完整演练
场景:已有 3 个主分区,把剩余空间全部划给扩展分区,再在其中划 2 个逻辑分区。
# 1) 创建扩展分区,占用第 4 个主分区位,大小用满剩余空间
Command (m for help): n
Command action
e extended
p primary partition (1-4)
e
Partition number (1-4): 4
First cylinder (…, default …): <回车>
Last cylinder, +cylinders or +size{K,M,G} (…, default …): <回车> # 全部剩余空间
# 2) 创建第一个逻辑分区,注意编号自动从 5 开始
Command (m for help): n
Command action
l logical (5 or over)
p primary partition (1-4)
l
First cylinder (…): <回车>
Last cylinder, +cylinders or +size{K,M,G}: +2G
# 3) 创建第二个逻辑分区(自动为 sdb6)
Command (m for help): n
l
First cylinder: <回车>
Last cylinder, +cylinders or +size{K,M,G}: +2G
# 4) 查看结果
Command (m for help): p
要点:
- 扩展分区本身不能格式化、不能挂载,
/dev/sdb4只是容器; - 逻辑分区从
/dev/sdb5开始; - 逻辑分区的总容量不能超过扩展分区;
- 若剩余空间已全部给扩展分区,就再也无法创建新的主分区。
修改分区类型 ID
Command (m for help): t
Partition number (1-6): 5
Hex code (type L to list all codes): 8e
Changed system type of partition 5 to 8e (Linux LVM)
步骤:先输入 t,再输入要修改的分区号,最后输入类型 ID。输入 L 可列出所有支持的分区类型。常用 ID:83(Linux,默认)、82(Linux swap)、8e(Linux LVM)、fd(Linux raid autodetect)、7(NTFS)、b(FAT32)、ef(EFI System)。
设置引导标志与删除分区
Command (m for help): a # 切换引导标志
Partition number (1-4): 1 # 将 sdb1 设为可引导(p 输出的 Boot 列出现 *)
Command (m for help): d # 删除分区
Partition number (1-6): 6 # 删除 sdb6
删除扩展分区会连带删除其内部所有逻辑分区;删除中间的逻辑分区会导致后续逻辑分区编号前移。
查看与保存
使用 p 查看当前分区表:
Command (m for help): p
Disk /dev/sdc: 5368 MB, 5368709120 bytes
255 heads, 63 sectors/track, 652 cylinders
Units = cylinders of 16065 * 512 = 8225280 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disk identifier: 0xd4a66feb
Device Boot Start End Blocks Id System
/dev/sdc1 1 132 1060258+ 83 Linux
- 确认无误后输入
w保存并退出(此时才真正写入磁盘); - 发现操作错误就输入
q放弃退出,磁盘不会有任何改动。这是 fdisk 最重要的安全阀。
非交互式(脚本化)用法
在自动化脚本中可以用管道喂给 fdisk 一串命令:
# 在 /dev/sdb 上创建一个占满整盘的主分区,类型 83
echo -e "n\np\n1\n\n\nw\n" | fdisk /dev/sdb
# 创建主分区并设置类型为 8e(LVM)
printf "n\np\n1\n\n\nt\n8e\nw\n" | fdisk /dev/sdb
# 更推荐使用 parted 的脚本模式(可读性更好)
parted -s /dev/sdb mklabel msdos
parted -s /dev/sdb mkpart primary ext4 1MiB 100%
# 或使用 sfdisk(按分区表描述批量创建,适合批量部署)
echo ',,83' | sfdisk /dev/sdb # 单个占满全盘的 83 分区
sfdisk -d /dev/sda > sda.layout # 导出分区布局
sfdisk /dev/sdb < sda.layout # 把相同布局套用到另一块盘
CentOS 6 与 7 差异:CentOS 6 的
fdisk不支持 GPT(会告警并可能损坏 GPT 表);CentOS 7 的fdisk(util-linux 2.23+)支持 GPT,且新增了g(创建 GPT 表)命令,但复杂 GPT 操作仍建议用gdisk/parted。
2.8 parted 完整实操
parted 同时支持 MSDOS(MBR)和 GPT 分区表;当磁盘使用 GPT 或容量大于 2TB 时,必须使用 parted(或 gdisk)而非 CentOS 6 的 fdisk。
关键区别:
parted的每条命令都是立即生效、立即写入磁盘的,没有 fdisk 的w保存 /q放弃机制。因此操作前必须再三确认设备名,rm一旦回车就无法撤销。
模式一:交互模式
parted /dev/sdc
若不指定磁盘,parted 会默认操作系统中排序最靠前的磁盘(危险,务必显式指定):
GNU Parted 2.1
使用 /dev/sdc
Welcome to GNU Parted! Type 'help' to view a list of commands.
(parted)
常用交互命令:
| 命令 | 作用 |
|---|---|
help |
查看帮助;help mkpart 查看单条命令帮助 |
select <设备> |
切换到另一块磁盘操作 |
mklabel <类型> |
创建分区表(gpt / msdos),会清空原有分区表 |
mktable |
同 mklabel |
mkpart |
创建一个新分区 |
print(可简写 p) |
查看当前磁盘的分区信息 |
print all |
查看所有磁盘 |
print free |
同时显示未使用的空闲区域 |
rm <编号> |
删除指定编号的分区(立即生效!) |
name <编号> <名称> |
给 GPT 分区设置名称 |
set <编号> <标志> on/off |
设置分区标志(boot、lvm、raid、esp、swap、hidden) |
unit <单位> |
设置显示单位(s 扇区、B、KB、MB、GB、TB、%、compact、chs、cyl) |
resizepart <编号> <结束位置> |
调整分区结束位置(扩大/缩小分区,不动文件系统) |
align-check opt <编号> |
检查分区是否最优对齐(4K 对齐检测) |
toggle <编号> <标志> |
翻转某个标志 |
rescue <起始> <结束> |
尝试恢复该区间内丢失的分区 |
quit(简写 q) |
退出 parted |
查看分区示例:
(parted) print
Model: VMware, VMware Virtual S (scsi)
Disk /dev/sdc: 21.5GB
Sector size (logical/physical): 512B/512B
Partition Table: pc98
Number Start End Size File system Name 标志
1 1003MB 10.0GB 8998MB sdc1 启动
Number 列即分区编号,rm 1 表示删除编号为 1 的分区。Partition Table: 行显示当前分区表类型(msdos/gpt/loop/unknown)。
模式二:命令行(脚本)模式
把交互命令直接作为参数传给 parted,适合脚本自动化:
parted /dev/sdc print # 查看
parted -s /dev/sdc mklabel gpt # -s 脚本模式,不交互确认
parted -s /dev/sdc mkpart primary ext4 1MiB 10GiB # 创建分区
parted -s /dev/sdc mkpart data 1MiB 100% # GPT 下第二个参数是分区名
parted -s /dev/sdc set 1 lvm on # 设置 lvm 标志
parted -s /dev/sdc rm 1 # 删除分区
parted -s -a optimal /dev/sdc mkpart primary 0% 100% # -a optimal 保证最优对齐
parted /dev/sdc unit GB print # 以 GB 为单位查看
parted -l # 列出所有磁盘分区
创建 GPT 分区表与 2TB 以上大分区的完整步骤
# 场景:一块 4TB 的新盘 /dev/sdb,要做成一个完整的 4TB 分区
# 1. 确认盘符与容量,避免操作错盘
lsblk -d
parted /dev/sdb print # 若提示 unrecognised disk label 说明是裸盘
# 2. 创建 GPT 分区表(MBR 无法管理 2TB 以上,必须 gpt)
parted /dev/sdb
(parted) mklabel gpt
Warning: The existing disk label on /dev/sdb will be destroyed and all data
on this disk will be lost. Do you want to continue?
是/Yes/否/No? Yes
# 3. 设置显示单位为 TB 便于阅读
(parted) unit TB
# 4. 创建占满整盘的分区(0% 100% 由 parted 自动对齐,最稳妥)
(parted) mkpart primary xfs 0% 100%
# 也可显式指定:mkpart primary xfs 1MiB 4001GB
# 5. 查看确认
(parted) print
Model: VMware Virtual disk (scsi)
Disk /dev/sdb: 4.00TB
Sector size (logical/physical): 512B/512B
Partition Table: gpt
Number Start End Size File system Name 标志
1 0.00TB 4.00TB 4.00TB primary
# 6. 检查 4K 对齐是否最优
(parted) align-check opt 1
1 aligned
# 7. 退出(parted 已即时写盘,无需 w)
(parted) quit
# 8. 刷新分区表并格式化挂载
partprobe /dev/sdb
mkfs.xfs /dev/sdb1 # CentOS 7 推荐 xfs(ext4 上限 50TB,xfs 更适合大盘)
mkdir -p /data
mount /dev/sdb1 /data
df -hT /data
一条命令完成(脚本化):
parted -s /dev/sdb mklabel gpt mkpart primary xfs 0% 100%
partprobe /dev/sdb; mkfs.xfs -f /dev/sdb1
parted 创建 MBR 分区
parted /dev/sdc
(parted) mklabel msdos # 创建 MBR 分区表
(parted) mkpart primary ext4 1MiB 2GiB # 主分区
(parted) mkpart primary ext4 2GiB 4GiB # 第二个主分区
(parted) mkpart extended 4GiB 100% # 扩展分区
(parted) mkpart logical ext4 4GiB 6GiB # 逻辑分区(自动编号 5)
(parted) print
(parted) quit
调整分区大小(resizepart)
# 把第 1 个分区的结束位置扩大到 20GB(分区后方必须有连续空闲空间)
parted /dev/sdb
(parted) print # 先确认分区号与当前结束位置
(parted) resizepart 1
End? [10.0GB]? 20GB
(parted) quit
# 分区扩大后还需要扩容文件系统
resize2fs /dev/sdb1 # ext4
xfs_growfs /data # xfs(传挂载点)
resizepart只改分区边界,不会动文件系统。缩小分区前必须先缩小文件系统,否则数据损坏;xfs 无法缩小,因此 xfs 分区不能缩。
parted 分区后的后续操作
使用 parted 创建好分区后,同样需要刷新分区表、格式化和挂载:
partx -a /dev/sdc # 刷新系统分区表(或 partprobe /dev/sdc)
mkfs.ext4 /dev/sdc1 # 格式化为 ext4
mount /dev/sdc1 /sdc # 挂载到 /sdc
df -h # 检查挂载情况
2.9 gdisk 简述
gdisk(GPT fdisk,yum install gdisk -y)是专为 GPT 设计的交互式工具,用法与 fdisk 高度相似,比 parted 更适合习惯 fdisk 的人:
gdisk /dev/sdb # 进入交互界面
gdisk -l /dev/sdb # 列出 GPT 分区表
常用交互命令:
| 命令 | 作用 |
|---|---|
? |
帮助菜单 |
p |
打印分区表 |
n |
新建分区(依次输入分区号、起始扇区、结束扇区或 +size、类型码) |
d |
删除分区 |
t |
修改分区类型码(GPT 类型码,如 8300 Linux、8200 swap、8e00 LVM、ef00 EFI、fd00 RAID) |
c |
修改分区名称 |
i |
显示分区详细信息 |
l |
列出所有已知类型码 |
v |
校验磁盘 |
r |
进入恢复/转换子菜单(含 MBR↔GPT 转换、从备份 GPT 头恢复) |
w |
保存并退出(会二次确认 Y) |
q |
不保存退出 |
GPT 常用类型码(注意与 MBR 的 1 字节 ID 不同):
| 类型码 | 含义 |
|---|---|
8300 |
Linux filesystem(默认) |
8200 |
Linux swap |
8e00 |
Linux LVM |
fd00 |
Linux RAID |
ef00 |
EFI System Partition(ESP) |
ef02 |
BIOS Boot Partition(GPT + BIOS 引导必需) |
0700 |
Microsoft basic data(NTFS/exFAT/FAT32) |
MBR 与 GPT 互转(高风险操作,先备份):
gdisk /dev/sdb # 打开 MBR 磁盘时 gdisk 会自动读入并准备转成 GPT,w 保存即完成转换
# GPT 转回 MBR:gdisk 中进入 r(recovery)子菜单,选择 g(convert GPT into MBR),再 w
sgdisk -g /dev/sdb # 非交互方式将 MBR 转换为 GPT
2.10 刷新分区表让内核识别新分区
使用 w 写入分区表后,内核可能尚未生成对应的设备文件(尤其当该磁盘上有分区正在使用时),常见提示:
WARNING: Re-reading the partition table failed with error 16: Device or resource busy.
The kernel still uses the old table. The new table will be used at the next reboot
or after you run partprobe(8) or kpartx(8)
保存后先验证分区设备是否已生成:
ls /dev/sdc*
# /dev/sdc /dev/sdc1
lsblk
cat /proc/partitions
若新分区设备未出现,用以下任一方式刷新:
partprobe # 通知内核重新读取所有磁盘的分区表(推荐)
partprobe /dev/sdb # 只刷新指定磁盘(更安全,避免影响其他盘)
partprobe -s # 显示摘要信息
partx -a /dev/sdb # 添加该磁盘上的所有分区到内核
partx -a /dev/sdb1 /dev/sdb # 添加指定分区
partx -d /dev/sdb # 从内核删除该磁盘的分区映射
partx -u /dev/sdb # 更新分区映射
partx -l /dev/sdb # 列出内核识别到的分区
kpartx -a /dev/sdb # device-mapper 方式添加分区映射(常用于 loop 设备、多路径盘)
kpartx -d /dev/sdb # 删除映射
kpartx -l disk.img # 列出镜像文件中的分区
reboot # 终极方法:重启(在有分区被占用无法刷新时使用)
partx -a的作用是告知系统"刚刚新建了分区,请重新识别"。
为什么有时必须重启? 当同一块磁盘上有分区已经被挂载或正在被使用(如根分区所在的 /dev/sda),内核出于安全考虑拒绝重新加载整张分区表(error 16 / Device or resource busy),此时:
- 优先尝试
partprobe /dev/sda; - 若失败,用
partx -a --nr 3 /dev/sda只添加新增的那个分区号(不动已有分区,通常能成功); - 都不行则安排窗口期
reboot。
处理 "Device or resource busy" 的排查思路:
lsblk /dev/sdb # 看是否有分区已挂载
mount | grep sdb # 找出挂载点
lsof /dev/sdb1 # 找出占用该设备的进程
fuser -mv /mnt # 找出占用挂载点的进程
dmsetup ls # 看是否被 LVM / multipath / LUKS 占用
swapon -s # 看是否被当作 swap 启用
cat /proc/mdstat # 看是否是 RAID 成员
umount /mnt # 卸载后再刷新
2.11 分区表的备份与恢复
备份 MBR(dd / head 两种写法)
# 1) 连同启动加载程序(MBR 全部 512 字节)一起备份
dd if=/dev/sda of=/backup/mbr_full.bin bs=512 count=1
head -c 512 /dev/sda > /some/where/boot_sector.bin # 等效写法
# 2) 只备份引导代码(前 446 字节,不含分区表)
dd if=/dev/sda of=/backup/mbr_boot.bin bs=446 count=1
# 3) 仅备份分区表(MBR 中偏移 446 开始的 64 字节)
dd if=/dev/sda of=/backup/mbr_parttable.bin bs=1 count=64 skip=446
head -c 510 /dev/sda | tail -c 64 > /some/where/partition_table.bin # 等效写法
备份文件一定要存到其他磁盘或远程主机,存在本盘上等于没备份。
恢复 MBR(慎用,会覆盖目标盘的 MBR)
# 恢复完整 512 字节(引导代码 + 分区表)
dd if=/backup/mbr_full.bin of=/dev/sda bs=512 count=1
dd if=./mbr.txt of=/dev/sda bs=1 count=512 # 逐字节写法,等效
# 只恢复分区表 64 字节,不动引导代码
dd if=/backup/mbr_parttable.bin of=/dev/sda bs=1 count=64 seek=446
# 恢复后刷新分区表
partprobe /dev/sda
fdisk -l /dev/sda
备份与恢复 GPT(sgdisk)
GPT 分区表远大于 512 字节,不能用 dd bs=512 count=1 备份,必须用 sgdisk:
# 备份 GPT 分区表到文件
sgdisk --backup=/backup/sdb-gpt.bin /dev/sdb
sgdisk -b /backup/sdb-gpt.bin /dev/sdb # 简写
# 从备份恢复
sgdisk --load-backup=/backup/sdb-gpt.bin /dev/sdb
sgdisk -l /backup/sdb-gpt.bin /dev/sdb # 简写
# 把 sdb 的分区结构复制到 sdc(做 RAID 成员盘时很实用)
sgdisk -R /dev/sdc /dev/sdb # 复制分区表
sgdisk -G /dev/sdc # 随机化 GUID,避免与源盘冲突
# 用主 GPT 修复损坏的备份 GPT(或反向)
sgdisk -e /dev/sdb # 把备份 GPT 移到盘尾(扩容后必做)
gdisk /dev/sdb # 交互式:r → b/c 从备份恢复主表
也可以用 parted/sfdisk 导出可读文本备份:
sfdisk -d /dev/sda > /backup/sda-partitions.txt # 导出为文本(MBR/GPT 都支持)
sfdisk /dev/sda < /backup/sda-partitions.txt # 按文本恢复
parted /dev/sda unit s print > /backup/sda-layout.txt # 人工可读的记录,便于手工重建
恢复演练(实验步骤)
# 1. 在实验盘 /dev/sdb 上创建两个分区并备份 MBR
fdisk /dev/sdb # 创建 sdb1、sdb2,w 保存
dd if=/dev/sdb of=/root/sdb-mbr.bin bs=512 count=1
# 2. 故意破坏分区表(只清掉 64 字节分区表,保留引导代码)
dd if=/dev/zero of=/dev/sdb bs=1 count=64 seek=446
partprobe /dev/sdb
fdisk -l /dev/sdb # 分区已"消失"
# 3. 从备份恢复
dd if=/root/sdb-mbr.bin of=/dev/sdb bs=512 count=1
partprobe /dev/sdb
fdisk -l /dev/sdb # 分区回来了
mount /dev/sdb1 /mnt && ls /mnt # 数据完好无损
结论:分区表丢失时数据并没有真正丢失(数据块还在原处),只要能恢复分区表的起止位置,数据就能恢复。若没有备份,可以用 testdisk、gpart、parted 的 rescue 命令扫描并猜测分区边界。
三、文件系统与挂载
3.1 文件系统的概念与作用
文件系统是磁盘上经过特定格式化的一片区域,操作系统通过它把裸块设备组织成"目录 + 文件"的层次结构,从而方便地查询和访问磁盘块。
注意:存取磁盘数据不一定需要文件系统,例如 swap 设备就没有文件系统,数据库也可以直接使用裸设备。
基本概念:
- 文件系统:磁盘上经过格式化的区域,负责记录"哪个文件的数据放在哪些块上"。
- 文件:文件系统中存储数据的命名对象;文件可以为空,但仍为操作系统提供元数据信息。
- 目录:一种特殊文件,其中登记了若干文件条目(目录项),支撑文件系统的层次结构。每个文件至少登记在一个目录中。
- 子目录:被包含在另一个目录中的目录。
- 文件名:标记文件的字符串,最长 255 个字符。
- 路径名:以
/连接多个文件名形成的字符串,如/etc/passwd。
文件系统层次结构
磁盘上的文件系统是一种层次结构,由目录和子目录构成,最顶层的目录称为根目录(/)。
Windows 与 Linux 文件系统对比:
Windows 文件系统(多根) Linux 文件系统(单根,树状)
硬盘 /
├── C:\ 分区 ├── bin
├── D:\ 分区 ├── sbin
├── E:\ 分区 ├── etc
└── F:\ 分区 ├── home
├── var
├── tmp
├── root
├── dev
└── ...
在 Linux 中所有分区、设备都挂载到根目录下的某一子目录,形成一棵完整的目录树,不存在 Windows 那样的多个盘符根。
根目录下各子目录(
/bin、/etc、/var…)的用途详见文末"附录 B:Linux 文件系统目录结构"。
3.2 inode、block、超级块与目录项
一个文件由两部分组成:
- i 节点(inode,索引节点):记录文件的元数据——权限(mode)、属主 UID/GID、大小、时间戳(atime/mtime/ctime)、链接数、以及数据块的位置指针。文件名不在 inode 里。
- 数据(data block):文件实际内容所在的磁盘块。
文件系统的整体布局(以 ext 系列为例):
| 结构 | 作用 |
|---|---|
| 引导块 Boot Block | 文件系统开头预留给引导程序的空间 |
| 超级块 Super Block | 记录整个文件系统的全局信息:块大小、总块数/空闲块数、总 inode 数/空闲 inode 数、挂载时间、挂载次数、文件系统状态(clean/dirty)、UUID、卷标等。超级块损坏文件系统就无法挂载,因此 ext 会在多个块组中保存备份超级块 |
| 块组描述符 Group Descriptor | 描述每个块组的 inode 表位置、位图位置等 |
| 块位图 Block Bitmap | 用位标记每个数据块是否已被占用 |
| inode 位图 Inode Bitmap | 标记每个 inode 是否已被使用 |
| inode 表 Inode Table | 存放所有 inode 结构,数量在格式化时固定(ext 系列) |
| 数据块 Data Block | 存放文件真实内容;目录也是文件,其数据块中存放的是"目录项" |
目录项(dentry):目录的数据块中存放的是 文件名 → inode 号 的映射表。因此:
- 文件名本身并不存于 inode 中,而是存在于其所在目录的条目里;
- 访问
/etc/passwd的过程是:根目录 inode → 根目录数据块 → 找到etc的 inode 号 → 读 etc 的 inode → 读 etc 的数据块 → 找到passwd的 inode 号 → 读 passwd 的 inode → 读数据块。
查看 inode 号与信息:
ls -i /etc/passwd # 查看 inode 号
ls -li /etc # 列出目录下所有文件的 inode 号
stat /etc/passwd # 查看文件的完整元数据(inode、块数、三个时间戳)
df -i # 查看各文件系统 inode 使用情况
find / -inum 1234567 # 按 inode 号反查文件(找硬链接的所有名字)
find / -inum 1234567 -delete # 删除文件名含特殊字符无法删除的文件
tune2fs -l /dev/sdb1 | grep -i inode # 查看 ext 文件系统 inode 参数
硬链接与软链接在文件系统层的实现
| 对比项 | 硬链接(hard link) | 软链接/符号链接(symbolic link) |
|---|---|---|
| 创建命令 | ln 源文件 链接名 |
ln -s 源文件 链接名 |
| 本质 | 在目录中新增一个"文件名 → 同一个 inode 号"的目录项 | 新建一个独立 inode,其数据块中存放目标的路径字符串 |
| inode 号 | 与源文件完全相同 | 与源文件不同 |
| 链接计数 | 源文件 inode 的 link count +1 | 不影响源文件的 link count |
| 跨文件系统 | 不能(inode 号只在单个文件系统内唯一) | 可以 |
| 链接目录 | 不能(除 . 和 ..,防止目录环) |
可以 |
| 删除源文件 | 数据仍可通过其他硬链接访问(link count 减到 0 且无进程打开时才真正释放) | 变成"断链"(dangling link),访问报 No such file or directory |
| 文件大小 | 与源文件相同 | 等于目标路径字符串的长度 |
ls -l 标识 |
与普通文件相同 | 首字符 l,并显示 -> 目标 |
ln /data/a.txt /data/a_hard # 硬链接
ln -s /data/a.txt /data/a_soft # 软链接
ls -li /data/ # 观察 inode 号与链接数的差异
stat /data/a.txt # Links: 2 表示有 2 个硬链接名
"删除文件"的真实含义:rm 只是从目录中移除目录项并把 inode 的 link count 减 1。只有当 link count 变为 0 并且没有任何进程还打开着该文件时,数据块才会被标记为空闲。这解释了两个现象:
- 删除了大文件但
df显示空间没释放(有进程仍持有 fd,见排错清单); - 目录写权限决定能否删除其中的文件,而不是文件本身的权限。
inode 耗尽的现象与排查
现象:创建新文件报 No space left on device,但 df -h 显示还有大量剩余空间。
原因:ext 系列文件系统的 inode 总数在格式化时就已固定(默认每 16KB 空间分配 1 个 inode)。如果文件系统里存在海量小文件(如 session 文件、邮件队列、缓存碎片、日志切片),inode 会先于 block 被用完。
排查与处理:
# 1. 确认是 inode 耗尽而不是空间耗尽
df -h # Use% 不高
df -i # IUse% 达到 100%
# 2. 定位哪个目录下小文件最多(找出 inode 消耗大户)
for d in /*; do echo -n "$d "; find $d -xdev -type f 2>/dev/null | wc -l; done
find /var -xdev -type d -exec sh -c 'echo "$(ls -A "$1" | wc -l) $1"' _ {} \; 2>/dev/null | sort -rn | head -20
du --inodes -x -d 1 /var | sort -rn | head # 新版 coreutils 支持 --inodes
# 3. 清理无用小文件(如 PHP session、邮件队列、旧日志)
find /var/lib/php/session -type f -mtime +7 -delete
find /var/spool/postfix/maildrop -type f -mtime +30 -delete
# 4. 长期方案:重新格式化时增大 inode 数量(会丢数据,需先备份)
mkfs.ext4 -i 4096 /dev/sdb1 # 每 4KB 空间一个 inode(4 倍密度)
mkfs.ext4 -N 2000000 /dev/sdb1 # 直接指定 inode 总数
mkfs.ext4 -T small /dev/sdb1 # 使用小文件优化模板
# 或改用 xfs:xfs 的 inode 是动态分配的,几乎不会 inode 耗尽
mkfs.xfs /dev/sdb1
CentOS 6 与 7 差异:ext4(CentOS 6 默认)的 inode 数量固定,格式化后无法增加;xfs(CentOS 7 默认)的 inode 动态分配,天然不存在 inode 耗尽问题(除非
imaxpct限制触发,可用xfs_growfs -m调整)。
3.3 常见文件系统对比
| 文件系统 | 日志 | 单文件/单卷上限 | 可否在线扩容/缩容 | 特点与适用场景 |
|---|---|---|---|---|
| ext2 | 无 | 2TB / 32TB | 扩容需卸载 / 可缩 | 无日志,速度略快但异常断电后 fsck 极慢;常用于 /boot(兼容性最好) |
| ext3 | 有 | 2TB / 32TB | 可扩 / 可缩 | ext2 + 日志,可与 ext2 相互转换(tune2fs -j),已被 ext4 取代 |
| ext4 | 有 | 16TB / 1EB(实际推荐 ≤50TB) | 在线可扩,缩容需卸载 | CentOS 6 默认。extent 映射、延迟分配、多块分配、无日志模式可选、支持 fsck 快速检查 |
| xfs | 有(元数据日志) | 8EB / 8EB | 在线可扩,不可缩 | CentOS 7 默认。高性能大文件与并发写、动态 inode、支持在线碎片整理(xfs_fsr)、配额功能强(含 project quota) |
| btrfs | CoW | 16EB | 可扩可缩 | 写时复制、内建快照与多设备 RAID、自校验;CentOS 7 中为技术预览,RHEL 8 已移除,生产慎用 |
| vfat / FAT32 | 无 | 4GB / 2TB | 否 | Windows 通用,跨平台 U 盘首选;单文件不能超过 4GB;无权限概念。Windows 下的 FAT32 在 Linux 中识别为 vfat |
| exfat | 无 | 16EB | 否 | 用于大容量 U 盘/SD 卡,突破 FAT32 的 4GB 限制;CentOS 需装 fuse-exfat、exfat-utils(EPEL 源) |
| ntfs / ntfs-3g | 有 | 16TB+ | 否(Linux 下) | Windows 主文件系统;Linux 读写需 ntfs-3g(FUSE 实现,性能一般) |
| swap | — | — | — | 交换空间,没有文件系统结构,用 mkswap 创建 |
| iso9660 | 无 | — | 只读 | 光盘/ISO 镜像标准格式,只读挂载 |
| udf | 无 | — | 只读 | DVD/蓝光光盘格式 |
| tmpfs | 无 | 受内存限制 | 可(mount -o remount,size=) |
基于内存(+swap)的临时文件系统,重启即失,如 /dev/shm、/run |
| proc / sysfs / devtmpfs | — | — | — | 伪文件系统,映射内核数据结构,不占磁盘 |
| NFS | — | — | — | Unix/Linux 网络文件共享协议 |
| CIFS / SMB | — | — | — | Windows 网络共享协议,Linux 用 cifs-utils 挂载 |
NFS 与 CIFS 属于网络文件系统,服务端配置与优化
> 详见 阶段09。
CentOS 6 与 7 的默认文件系统差异(重要):
| 项目 | CentOS 6 | CentOS 7 |
|---|---|---|
| 默认文件系统 | ext4 | xfs |
/boot 默认 |
ext4 | xfs(也常见 ext4/ext2) |
| 创建命令 | mkfs.ext4 |
mkfs.xfs |
| 在线扩容 | resize2fs(可扩可缩,缩容须卸载) |
xfs_growfs(只能扩,不能缩) |
| 修复命令 | e2fsck -f -y |
xfs_repair |
| 查看信息 | dumpe2fs、tune2fs -l |
xfs_info、xfs_admin |
| 调整卷标 | e2label / tune2fs -L |
xfs_admin -L |
| 配额工具 | quota 系列(quotacheck/edquota/repquota) |
xfs_quota(也支持部分 quota 命令) |
| 备份工具 | dump / restore |
xfsdump / xfsrestore |
| 碎片整理 | 基本无需 | xfs_fsr |
选型建议:CentOS 7 上除有明确缩容需求的场景外,一律使用默认 xfs;若业务确实需要将来缩小文件系统,必须选 ext4(或使用 LVM 精简卷灵活分配)。
3.4 格式化(创建文件系统)
创建文件系统就是常说的"分区格式化",使用 mkfs 系列命令。分区完成后仍不能直接使用,还必须经过格式化和挂载。
mkfs 命令格式
mkfs -t <文件系统类型> <设备>
mkfs.<类型> <设备> # 等价的简写形式
以下两条命令等价,都将分区 /dev/sdc1 格式化为 ext4:
mkfs -t ext4 /dev/sdc1
mkfs.ext4 /dev/sdc1
各类型格式化示例:
mkfs -t ext2 /dev/sda3 # 格式化 boot 分区为 ext2
mkfs.ext3 /dev/sdb1 # ext3
mkfs.ext4 /dev/sda6 # 格式化 home 分区为 ext4(CentOS 6 默认)
mkfs.xfs /dev/sdb1 # xfs(CentOS 7 默认)
mkfs.xfs -f /dev/sdb1 # -f 强制覆盖已有文件系统
mkfs.vfat /dev/sda2 # 格式化为 FAT32(Linux 中识别为 vfat)
mkfs.vfat -F 32 /dev/sdb1 # 明确指定 FAT32
mkfs.ntfs /dev/sda2 # 格式化为 NTFS(较慢)
mkfs.ntfs -f /dev/sda2 # 快速格式化 NTFS(-f/-Q 跳过全盘清零)
mkfs.btrfs /dev/sdb1 # btrfs
mkswap /dev/sda5 # 格式化为交换空间(无文件系统)
mkfs -t ext4 /dev/mapper/vg0-lv0 # 格式化 LVM 逻辑卷
mkfs.xfs /dev/md0 # 格式化 RAID 阵列
查看系统支持哪些 mkfs:
ls /sbin/mkfs*
# mkfs mkfs.btrfs mkfs.cramfs mkfs.ext2 mkfs.ext3 mkfs.ext4 mkfs.minix mkfs.vfat mkfs.xfs
cat /proc/filesystems # 查看当前内核支持的文件系统
mkfs / mke2fs 常用选项(ext 系列)
mkfs.ext4 实际就是 mke2fs -t ext4,两者选项通用:
| 选项 | 作用 |
|---|---|
-t <类型> |
指定文件系统类型(ext2/ext3/ext4/xfs/vfat…) |
-b <字节> |
指定块大小,可选 1024、2048、4096(默认 4096)。小文件多用小块省空间,大文件用 4096 性能好 |
-L <卷标> |
设置卷标(LABEL),便于在 fstab 中用 LABEL= 挂载 |
-i <字节> |
每多少字节数据分配一个 inode(值越小 inode 越多),如 -i 4096 |
-N <数量> |
直接指定 inode 总数 |
-m <百分比> |
为 root 保留的块百分比(默认 5%),大盘可设 -m 1 甚至 -m 0 节省空间 |
-T <类型> |
使用预设模板:small(小文件多)、largefile(每 1MB 一个 inode)、largefile4(每 4MB 一个 inode)、news、floppy |
-j |
创建带日志的文件系统(ext3) |
-c |
格式化前检查坏道(-cc 做读写检查,很慢) |
-F |
强制执行(即使目标看起来不是块设备或已挂载) |
-n |
只模拟不实际写入(用于预演、查看备份超级块位置),非常安全 |
-q |
静默模式 |
-v |
详细输出 |
-U <uuid> |
指定 UUID(-U random 随机生成) |
-O <特性> |
启用/禁用文件系统特性,如 -O ^has_journal 关闭日志 |
-E <扩展选项> |
扩展选项,如 -E stride=16,stripe-width=64(RAID 对齐优化)、-E lazy_itable_init=1(加速格式化) |
示例:
# 大量小文件的场景:块 1K + 高密度 inode + 保留 1%
mkfs.ext4 -b 1024 -i 4096 -m 1 -L smallfiles /dev/sdb1
# 存放大文件(视频、备份镜像):减少 inode 数量,省空间
mkfs.ext4 -T largefile -m 0 -L bigdata /dev/sdb1
# 指定卷标并查看备份超级块位置(模拟不写盘)
mkfs.ext4 -n /dev/sdb1 | grep -A2 "Backup superblock"
mkfs.xfs 常用选项
| 选项 | 作用 |
|---|---|
-f |
强制覆盖已存在的文件系统(xfs 默认拒绝覆盖,重做实验时必用) |
-L <卷标> |
设置卷标(最长 12 字符) |
-b size=<值> |
数据块大小,如 -b size=4096 |
-i size=<值> |
inode 大小(256~2048) |
-d agcount=<n> |
分配组数量,影响并发性能 |
-d su=,sw= |
RAID 条带对齐(su 条带单元、sw 数据盘数量) |
-l size=<值> |
日志区大小;-l logdev=/dev/sdX 把日志放到独立设备(SSD)提升性能 |
-n size=<值> |
目录块大小,目录内文件极多时可增大 |
-m crc=1 |
启用元数据 CRC 校验(CentOS 7 默认开启) |
-q |
静默 |
mkfs.xfs -f -L data -b size=4096 /dev/sdb1
mkfs.xfs -f -d su=64k,sw=3 /dev/md0 # 与 RAID5(3 数据盘、64K 条带)对齐
格式化前的确认习惯(避免灾难)
格式化是不可逆操作,务必养成以下习惯:
# 1. 确认设备身份:容量、型号、是否是你以为的那块盘
lsblk -o NAME,SIZE,TYPE,FSTYPE,MOUNTPOINT,SERIAL /dev/sdb
ls -l /dev/disk/by-id/ | grep sdb
# 2. 确认该设备上没有数据、没有已有文件系统
blkid /dev/sdb1 # 有输出说明已有文件系统,务必确认可以覆盖
lsblk -f /dev/sdb
# 3. 确认没有被挂载、没有被 LVM/RAID/swap 占用
mount | grep sdb
swapon -s | grep sdb
pvs | grep sdb
cat /proc/mdstat
# 4. 关键盘先备份分区表与关键数据
dd if=/dev/sdb of=/backup/sdb-mbr.bin bs=512 count=1
# 5. 复制粘贴命令后,回车前再读一遍设备名(sda 与 sdb 只差一个字母)
血泪教训:
mkfs.ext4 /dev/sda(漏写分区号)会直接摧毁整块盘的分区表;mkfs.ext4 /dev/sda1打成/dev/sda2会格式化掉生产数据。格式化命令永远不要凭记忆盲打。
3.5 挂载(mount)
格式化完成后,分区仍需要挂载到系统目录下才能使用。
完整语法
mount [选项] <设备> <挂载点>
mount -t <类型> -o <挂载选项> <设备> <挂载点>
讲义原文中参数顺序写作
mount [选项] 挂载点 需挂载介质,实际使用的标准顺序为设备 挂载点。
主要选项:
| 选项 | 作用 |
|---|---|
-t <类型> |
指定挂载文件系统类型,可省略(mount 会通过 blkid 自动识别) |
-o <选项> |
指定挂载选项,可省略(使用默认值 defaults),多个选项用逗号分隔 |
-a |
挂载 /etc/fstab 中所有尚未挂载的条目 |
-r |
只读挂载(等价 -o ro) |
-w |
读写挂载(等价 -o rw,默认) |
-L <卷标> |
按卷标挂载,如 mount -L mydata /data |
-U <UUID> |
按 UUID 挂载 |
-n |
挂载时不写入 /etc/mtab(根分区只读时使用) |
-v |
详细输出 |
-f |
模拟挂载(配合 -v 排查问题,不真正挂载) |
--bind |
绑定挂载(等价 -o bind) |
-B / -R |
--bind / --rbind(递归绑定,含子挂载) |
-o remount |
重新挂载(用于修改已挂载文件系统的选项) |
-o 挂载选项全表
| 选项 | 含义 |
|---|---|
ro |
只读挂载 |
rw |
可读可写挂载(默认) |
exec / noexec |
允许 / 禁止执行该文件系统上的二进制程序(/tmp、/var 建议 noexec 加固) |
suid / nosuid |
允许 / 禁止 SUID、SGID 位生效(安全加固常用 nosuid) |
dev / nodev |
允许 / 禁止解释设备文件(挂载不可信介质必加 nodev) |
auto / noauto |
mount -a 时自动挂载 / 不自动挂载(光驱、U 盘常用 noauto) |
atime / noatime |
记录 / 不记录文件访问时间。noatime 可显著减少写 I/O,SSD 与高并发读场景推荐 |
diratime / nodiratime |
目录访问时间是否更新 |
relatime |
折中方案:仅当 atime 早于 mtime/ctime 或超过 1 天才更新(现代内核默认) |
strictatime |
严格按 POSIX 更新 atime |
sync |
同步写入,每次写操作立即落盘(安全但极慢,U 盘可用) |
async |
异步写入,先写缓存后台落盘(默认,性能好) |
defaults |
等价于 rw,suid,dev,exec,auto,nouser,async(最常用) |
remount |
重新挂载已挂载的文件系统,用于在线修改选项 |
loop |
将一个普通文件作为块设备挂载(常用于 ISO 镜像、大文件当磁盘) |
bind |
绑定目录,把一个目录挂载到另一个位置 |
user / nouser |
允许 / 不允许普通用户挂载 |
users |
允许任何用户挂载和卸载 |
owner |
允许设备属主挂载 |
uid=<n> / gid=<n> |
指定挂载后所有文件的属主/属组(用于 vfat/ntfs 这类无权限概念的文件系统) |
umask= / dmask= / fmask= |
vfat/ntfs 的权限掩码(目录/文件分别控制) |
iocharset=utf8 |
字符集,解决 vfat/ntfs/iso9660 中文名乱码 |
_netdev |
标记为网络设备,系统会等网络就绪后再挂载(NFS/iSCSI 必加) |
nofail |
设备不存在时不报错、不阻塞启动(可插拔设备、数据盘强烈推荐) |
errors=remount-ro |
ext 系列出错时自动重挂为只读(根分区常用) |
data=journal/ordered/writeback |
ext3/4 日志模式:全数据日志(最安全最慢)/ 默认 / 最快最不安全 |
barrier=0/1 |
写屏障,带电池的 RAID 卡可设 barrier=0 提升性能 |
discard |
删除文件时向 SSD 发送 TRIM(更推荐用 fstrim.timer 定时) |
usrquota / grpquota |
启用用户/组磁盘配额(ext 系列) |
uquota / gquota / pquota |
xfs 的用户/组/项目配额 |
x-systemd.automount |
首次访问挂载点时才挂载(systemd 按需挂载) |
x-systemd.device-timeout=5 |
等待设备的超时时间 |
noquota |
禁用配额 |
size=<值> |
tmpfs 大小限制,如 size=2G |
使用示例
# 查看当前系统所有挂载信息
mount
# 最简形式(类型自动识别)
mount /dev/sdc1 /sdc
# 指定类型与选项
mount -t ext2 -o rw /dev/sda3 /disk/boot
mount -t ext4 -o defaults,noatime /dev/sdb1 /data
mount -t xfs /dev/sdb1 /data
# 只读挂载(取证、检查可疑磁盘时必用)
mount -o ro /dev/sdb1 /mnt
mount -r /dev/sdb1 /mnt
# 安全加固挂载(禁止执行、禁止 SUID、禁止设备文件)
mount -o defaults,noexec,nosuid,nodev /dev/sdb1 /data
# 挂载 vfat(Windows C 盘),指定属主与中文字符集
mount -t vfat /dev/sda2 /disk/C
mount -t vfat -o uid=1000,gid=1000,iocharset=utf8 /dev/sdb1 /media/usb
# 挂载 NTFS 分区(需安装 ntfs-3g,位于 EPEL 源)
wget -O /etc/yum.repos.d/epel.repo http://mirrors.aliyun.com/repo/epel-7.repo
yum install ntfs-3g -y
mount.ntfs-3g /dev/sda2 /disk/D
mount -t ntfs-3g /dev/sda2 /disk/D
# 挂载 NFS 网络文件系统
mount -t nfs 192.168.0.183:/soul /mnt
# 挂载 ISO 镜像(loop 方式)
mount -o loop /soft/CentOS6.5-x64.iso /iso
mount -t iso9660 -o ro,loop /soul/images/centos.iso /yum
# 挂载光驱
mount /dev/sr0 /mnt/cdrom
mount /dev/cdrom /media/cdrom
# 目录绑定(访问 /mnt 即访问 /soul)
mount -o bind /soul /mnt
mount --bind /data/www /var/www/html
# 重新挂载(修改挂载选项或修复只读根分区)
mount -o remount,rw /dev/sda1
mount -o remount,rw / # 救援模式下把只读根分区改为可写
mount -o remount,noatime /data # 在线加上 noatime
mount -o remount,ro /data # 在线改为只读(备份前保证一致性)
# 挂载 tmpfs(内存文件系统)
mount -t tmpfs -o size=2G tmpfs /tmp/ramdisk
挂载点的要求与挂载覆盖现象
挂载点必须是一个已存在的目录:
mkdir -p /data # 挂载点不存在需先创建
mount /dev/sdb1 /data
挂载覆盖现象:如果挂载点目录在挂载前已有文件,挂载后这些文件会被"遮挡"——它们并没有被删除,只是暂时看不到,卸载后又会重新出现。
mkdir /data && touch /data/old.txt
ls /data # old.txt
mount /dev/sdb1 /data
ls /data # 只有 lost+found,old.txt 不见了(被遮挡)
umount /data
ls /data # old.txt 又回来了
因此:挂载点必须使用空目录,否则原有文件在挂载期间无法访问,还会白白占用底层文件系统空间(用 du 查不到,是磁盘空间"神秘消失"的常见原因之一)。
排查被遮挡数据的方法:
mkdir /tmp/probe
mount --bind / /tmp/probe # 把根文件系统绑定到别处,绕过所有挂载点查看
du -sh /tmp/probe/data # 看看挂载点下面藏了多少东西
umount /tmp/probe
其它注意事项:
- 同一个设备可以挂载到多个挂载点;同一个挂载点也可以叠加挂载多个设备(后挂载的遮挡前面的)。
- 正在使用的目录(有进程 cwd 在其中)不能作为挂载点被卸载。
- 不要把设备挂载到
/、/etc、/usr等系统关键目录上。
查看已挂载信息
mount # 输出格式:挂载源 on 挂载目标 type 类型 (挂载选项)
mount -l # 附带显示卷标
mount -t ext4 # 只列出 ext4 类型的挂载
cat /proc/mounts # 内核视角的真实挂载表(最权威)
cat /etc/mtab # 用户空间维护的挂载表
df -hT # 简要挂载信息 + 类型 + 使用率
findmnt # 树形展示所有挂载点(CentOS 7 推荐)
findmnt /data # 查看指定挂载点详情
findmnt -t xfs # 按类型过滤
findmnt -S /dev/sdb1 # 按源设备查找
findmnt -T /data/file # 查某个路径属于哪个挂载点
findmnt --verify # 校验 /etc/fstab 语法是否正确(改完 fstab 必做)
findmnt -D # 类似 df 的输出格式
findmnt -o TARGET,SOURCE,FSTYPE,OPTIONS # 自定义列
3.6 卸载(umount)
umount /sdc # 按挂载点卸载(推荐)
umount /dev/sdc1 # 按设备卸载
umount -l /mnt # 延迟卸载(lazy umount):立即从目录树摘除,等设备不忙时真正卸载
umount -f /mnt # 强制卸载(主要用于失联的 NFS)
umount -a # 卸载 /etc/mtab 中所有文件系统(危险)
umount -R /mnt # 递归卸载该挂载点及其下所有子挂载
umount -v /mnt # 详细输出
卸载时需确保当前没有进程在使用该挂载点,否则报错:
umount: /data: target is busy.
(In some cases useful info about processes that use
the device is found by lsof(8) or fuser(1))
"target is busy" 的完整排查流程
# 0. 最常见原因:自己的 shell 正处在挂载点里
pwd
cd / # 先退出该目录再试
# 1. 用 lsof 找出占用该挂载点的进程
lsof /data
lsof +D /data # 递归检查目录下所有文件(较慢但彻底)
lsof | grep /data
# 2. 用 fuser 找出占用进程(-m 表示按挂载点查找,-v 详细显示)
fuser -mv /data
# USER PID ACCESS COMMAND
# root 1234 ..c.. bash <- c=当前工作目录 cwd
# root 5678 F.... vim <- f/F=打开的文件
# ACCESS 含义:c=cwd, e=可执行文件, f=打开的文件, F=打开且在写, r=root 目录, m=mmap
# 3. 温和处理:通知相关进程退出,或手工结束
fuser -mk -TERM /data # 向占用进程发送 SIGTERM(可被捕获、可优雅退出)
kill <PID>
# 4. 强制处理(有数据丢失风险,确认无重要写入后再用)
fuser -km /data # 杀掉所有占用该挂载点的进程(默认 SIGKILL)
fuser -ki /data # 逐个询问后再杀
umount /data
# 5. 仍失败则用延迟卸载
umount -l /data
# 之后用 findmnt / cat /proc/mounts 确认是否已真正卸载
# 6. 其它易忽略的占用来源
swapon -s | grep /data # 挂载点内有 swap 文件被启用 → swapoff
exportfs -v # 该目录被 NFS 导出 → exportfs -u
pvs; lvs; dmsetup ls # 被 LVM/LUKS 占用
cat /proc/mdstat # 是 RAID 成员
docker ps; ctr c ls # 容器挂载了该目录
systemctl status <相关服务> # 服务持有文件 → systemctl stop
完成以上步骤后,新建分区即可正常读写。
3.7 开机自动挂载 /etc/fstab
使用 mount 命令手动挂载的分区在系统重启后会失效。若要实现开机自动挂载,需要将配置写入 /etc/fstab。
文件示例
cat /etc/fstab
CentOS 6 典型内容:
UUID=b40cacb1-1178-4228-be06-e6d2f135b23e / ext4 defaults 1 1
UUID=174c6b5a-0de6-469d-bc07-b38d4bfd3479 /boot ext4 defaults 1 2
UUID=d608f49e-d50c-41ad-bf9a-7f7a0ef8707d swap swap defaults 0 0
tmpfs /dev/shm tmpfs defaults 0 0
devpts /dev/pts devpts gid=5,mode=620 0 0
sysfs /sys sysfs defaults 0 0
proc /proc proc defaults 0 0
其它常见写法示例(含 vfat、iso、swap 的 none 挂载点):
UUID=db6a4392-ccb0-41af-863f-4de76cad1c0a / ext4 errors=remount-ro 0 1
UUID=67a2f488-1421-495a-ada1-dc022c29d77d none swap sw 0 0
UUID=96A4-6194 /disk/C vfat defaults 0 0
UUID=51c35f8c-c9dd-498e-9f05-203d6f931fa6 /disk/boot ext2 defaults 0 0
UUID=c9edda7d-dbc2-443a-81a4-5a729fc9b27a none swap sw 0 0
/disk/ubuntu-16.04.4-server-amd64.iso /opt iso9660 loop,ro 0 0
CentOS 7 常见内容(LVM + xfs):
/dev/mapper/centos-root / xfs defaults 0 0
UUID=51c35f8c-c9dd-498e /boot xfs defaults 0 0
/dev/mapper/centos-swap swap swap defaults 0 0
UUID=8f5c1c2a-6b3e-4c7a /data xfs defaults,noatime,nofail 0 0
192.168.1.10:/share /mnt/nfs nfs defaults,_netdev 0 0
六个字段逐项详解
格式:
挂载源(设备) 挂载目标(挂载点) 文件系统类型 挂载选项 dump备份 开机自检顺序
| 列序 | 字段 | 含义与取值 |
|---|---|---|
| 第 1 列 | 要挂载的设备(挂载源) | 可写设备名(/dev/sdb1)、LABEL=卷标、UUID=xxx、PARTUUID=xxx、网络地址(192.168.1.10:/share)、/dev/mapper/vg-lv、伪文件系统名(tmpfs/proc/sysfs/devpts)。强烈推荐 UUID |
| 第 2 列 | 挂载点 | 设备挂载到的目录,必须是已存在的绝对路径。swap 没有挂载点,此处写 swap 或 none(两种写法都可以) |
| 第 3 列 | 文件系统类型 | ext2/ext3/ext4/xfs/vfat/ntfs-3g/swap/iso9660/tmpfs/proc/sysfs/devpts/nfs/cifs。也可写 auto 让系统自动探测 |
| 第 4 列 | 挂载参数 | 常用 defaults;也可用 ro、rw、noatime、noauto、loop、nofail、_netdev、usrquota,grpquota、errors=remount-ro。swap 常写 sw 或 defaults。多个选项用逗号分隔且不能有空格 |
| 第 5 列 | dump 备份标志 | 0 不备份;1 表示 dump 命令会备份该文件系统。现代环境基本都写 0 |
| 第 6 列 | fsck 自检顺序 | 0 开机不检查;1 优先级最高(只给根分区 /);2 次优先(其他分区,可并行检查)。xfs 一般写 0(xfs 靠日志自愈,不做开机 fsck) |
第一列设备的三种写法
第一列支持三种写法,效果等价:
1. 直接使用设备名
/dev/sdc1 /sdc ext4 defaults 0 0
缺点:设备名会因磁盘增删、插拔顺序、驱动加载顺序而变化,一旦 sdb 变成 sdc,开机就会挂载失败甚至无法启动。生产环境不推荐。
2. 使用 LABEL(卷标)
卷标是用户自定义的名称。
查看卷标(ext 系列):
e2label /dev/sdc1
若未设置过卷标或分区未格式化,会提示:
e2label: Bad magic number in super-block 当尝试打开 /dev/sdc1 时找不到有效的文件系统超级块.
设置卷标:
e2label /dev/sdc1 /abc # ext 系列设置卷标
e2label /dev/sdc1 # 再次查看,输出 /abc
tune2fs -L mydata /dev/sdc1 # ext 系列另一种写法
xfs_admin -L mydata /dev/sdb1 # xfs 设置卷标(必须先卸载)
xfs_admin -l /dev/sdb1 # xfs 查看卷标
fatlabel /dev/sdb1 MYUSB # vfat 设置卷标
blkid | grep LABEL # 统一查看所有卷标
在 fstab 中使用:
LABEL=/abc /sdc ext4 defaults 0 0
LABEL=mydata /data xfs defaults 0 0
3. 使用 UUID(推荐)
UUID(Universally Unique Identifier)是格式化时生成的分区唯一标识,推荐使用。
查看 UUID:
blkid # 查看所有分区的 UUID
blkid /dev/sdc1 # 查看指定分区
ls -l /dev/disk/by-uuid/
lsblk -f
/dev/sda1: LABEL="/vm" UUID="174c6b5a-0de6-469d-bc07-b38d4bfd3479" TYPE="ext4"
在 fstab 中使用:
UUID=174c6b5a-0de6-469d-bc07-b38d4bfd3479 /sdc ext4 defaults 0 0
为什么推荐 UUID 而不是 /dev/sdb1:
- 设备名称(如
/dev/sda1)可能因硬盘变动(删除中间分区、添加/移除硬盘、更换 HBA、驱动加载顺序变化、虚拟机迁移)而发生改变; - UUID 是分区(准确说是文件系统)的唯一标识符,只要不重新格式化,该分区的 UUID 就保持唯一、不会冲突,跟着数据走;
- 用 UUID 写 fstab 后,即使磁盘换了槽位、盘符从 sdb 变成 sdd,依然能正确挂载。
修改 UUID:
uuidgen # 生成一个新 UUID
tune2fs -U $(uuidgen) /dev/sdb1 # 修改 ext 系列的 UUID
tune2fs -U random /dev/sdb1 # 随机生成
xfs_admin -U generate /dev/sdb1 # 修改 xfs 的 UUID(克隆虚拟机后必做,否则 UUID 冲突)
mount -a 验证的重要习惯
vim /etc/fstab # 修改配置
findmnt --verify # 先做语法校验(CentOS 7)
mount -a # 挂载 fstab 中所有未挂载的条目
df -h # 确认挂载成功
mount | grep /data # 确认挂载选项生效
铁律:修改
/etc/fstab后必须立即执行mount -a验证配置是否正确,若报错就当场修好。否则错误只会在下次重启时暴露,而那时系统可能已经起不来了。
对于非关键的数据盘,建议加上 nofail(设备缺失也不阻塞启动):
UUID=xxx /data xfs defaults,nofail 0 0
写错 fstab 导致无法启动的救援方法
现象:开机停在 Welcome to emergency mode! 或 Give root password for maintenance,或者提示 Cannot open access to console, the root account is locked。
CentOS 7 救援步骤(emergency mode):
# 1. 输入 root 密码进入紧急模式(emergency shell)
# 2. 此时根分区是只读的,先重新挂载为可读写
mount -o remount,rw /
# 3. 编辑修复 fstab(把出错的行注释掉或改正)
vi /etc/fstab
# 4. 验证
mount -a
findmnt --verify
# 5. 重启
reboot
# 或直接继续启动:systemctl default / exit
若连紧急模式都进不去(root 密码未知或账户被锁):
1. 开机在 GRUB 菜单按 e 编辑启动项
2. 找到 linux16 / linux 开头的那一行,在行尾追加:
rd.break (进入 initramfs 阶段,CentOS 7)
或 init=/bin/bash (直接以 bash 为 1 号进程)
或 systemd.unit=emergency.target
3. Ctrl+X 启动
4. 若用 rd.break:
mount -o remount,rw /sysroot
chroot /sysroot
vi /etc/fstab # 修复
touch /.autorelabel # 若开启 SELinux
exit; exit # 重启
5. 若用 init=/bin/bash:
mount -o remount,rw /
vi /etc/fstab
exec /sbin/init # 或 reboot -f
CentOS 6 救援步骤:
1. 开机时进入单用户模式:GRUB 菜单按 a 或 e,在内核行尾加 single(或 1)
2. 进入后执行:
mount -o remount,rw /
vi /etc/fstab
reboot
3. 若单用户模式也进不去,用光盘/U 盘 Linux Rescue 模式:
选择 "Rescue installed system"
系统会把原系统挂载到 /mnt/sysimage
chroot /mnt/sysimage
vi /etc/fstab
exit; reboot
预防措施:
- 改 fstab 前先备份:
cp /etc/fstab /etc/fstab.bak.$(date +%F); - 改完立刻
mount -a+findmnt --verify; - 非根分区一律加
nofail; - 网络存储加
_netdev,nofail; - 一律使用 UUID。
systemd 的 .mount 单元与按需挂载(CentOS 7)
CentOS 7 的 systemd 会把 /etc/fstab 自动转换成 .mount 单元(单元名由挂载点路径生成,/ 换成 -):
systemctl list-units --type=mount # 列出所有挂载单元
systemctl status data.mount # 查看 /data 的挂载单元状态
systemctl daemon-reload # 修改 fstab 后让 systemd 重新生成单元(重要)
systemctl start data.mount # 挂载
systemctl stop data.mount # 卸载
也可以手写 .mount 单元(/etc/systemd/system/data.mount):
[Unit]
Description=Mount /data
After=local-fs.target
[Mount]
What=/dev/sdb1
Where=/data
Type=xfs
Options=defaults,noatime
[Install]
WantedBy=multi-user.target
按需自动挂载(automount):只有当有进程访问挂载点时才真正挂载,空闲一段时间后自动卸载。适合光驱、NFS、移动介质。
在 fstab 中启用:
UUID=xxx /data xfs defaults,x-systemd.automount,x-systemd.idle-timeout=60,x-systemd.device-timeout=5,nofail 0 0
192.168.1.10:/share /mnt/nfs nfs x-systemd.automount,_netdev,noauto 0 0
对应的 .automount 单元写法:
# /etc/systemd/system/data.automount
[Unit]
Description=Automount /data
[Automount]
Where=/data
TimeoutIdleSec=60
[Install]
WantedBy=multi-user.target
systemctl enable --now data.automount
CentOS 6 对应的按需挂载机制是
autofs服务(/etc/auto.master+/etc/auto.misc),service autofs start。
3.8 loop 设备:ISO 镜像与"大文件当磁盘用"
挂载 ISO 镜像
mount -o loop /soft/CentOS6.5-x64.iso /iso
mount -t iso9660 -o ro,loop /soul/images/centos.iso /yum
umount /iso
写入 fstab 长期挂载(常用于搭建本地 yum 源):
/soft/CentOS7.iso /mnt/iso iso9660 loop,ro 0 0
loop 设备管理:
losetup -a # 查看所有已使用的 loop 设备
losetup -f # 显示第一个空闲的 loop 设备名
losetup /dev/loop0 disk.img # 手工把文件关联到 loop 设备
losetup -d /dev/loop0 # 释放 loop 设备
losetup -P /dev/loop0 disk.img # -P 让内核识别镜像内的分区(生成 loop0p1)
kpartx -av disk.img # 另一种识别镜像内分区的方法
创建一个大文件当磁盘用(完整演练)
用途:测试文件系统、模拟磁盘、做 swap 文件、给容器/用户做空间隔离。
# 1. 创建一个 1GB 的空文件
dd if=/dev/zero of=/data/disk.img bs=1M count=1024
# if=/dev/zero 输入源为零设备
# of=... 输出文件路径
# bs=1M 每次写入 1M
# count=1024 写入 1024 次,共 1024M(1G)
# 更快的等价做法(稀疏文件,瞬间完成但不预占空间)
dd if=/dev/zero of=/data/disk.img bs=1M count=0 seek=1024
truncate -s 1G /data/disk.img
fallocate -l 1G /data/disk.img # 预分配真实空间(ext4/xfs 支持,最推荐)
head -c 1G /dev/zero > /data/disk.img # 用 head 生成
# 2. 在该文件上创建文件系统(会提示 not a block device,输入 y 继续)
mkfs.ext4 /data/disk.img
# 或 mkfs.xfs -f /data/disk.img
# 3. 挂载使用
mkdir -p /mnt/vdisk
mount -o loop /data/disk.img /mnt/vdisk
df -hT /mnt/vdisk
# 4. 写入 fstab 实现开机自动挂载
echo '/data/disk.img /mnt/vdisk ext4 loop,defaults 0 0' >> /etc/fstab
mount -a
# 5. 扩容这个"虚拟磁盘"
umount /mnt/vdisk
dd if=/dev/zero bs=1M count=1024 >> /data/disk.img # 追加 1G
# 或 truncate -s 2G /data/disk.img
e2fsck -f /data/disk.img
resize2fs /data/disk.img # ext4 扩容文件系统
mount -o loop /data/disk.img /mnt/vdisk
# 6. 用完清理
umount /mnt/vdisk
rm -f /data/disk.img
该文件是二进制文件,无法使用
cat或vim正常查看和编辑。
3.9 文件系统检查与修复
铁律:
fsck/e2fsck/xfs_repair只能对未挂载(或只读挂载)的文件系统操作。对已挂载的读写文件系统执行检查修复会造成严重的文件系统损坏。
fsck(通用前端)
umount /dev/sdb1 # 先卸载!
fsck /dev/sdb1 # 自动按类型调用 e2fsck / xfs_repair 等
fsck -t ext4 /dev/sdb1 # 指定类型
fsck -y /dev/sdb1 # 对所有问题自动回答 yes(无人值守)
fsck -a /dev/sdb1 # 自动修复,不提问(比 -y 保守)
fsck -n /dev/sdb1 # 只检查不修复(只读方式,可用于已挂载的粗略检查)
fsck -f /dev/sdb1 # 强制检查(即使标记为 clean)
fsck -A # 检查 /etc/fstab 中所有文件系统
fsck -AR -y # 检查除根分区外的所有(-R 跳过根)
fsck -C /dev/sdb1 # 显示进度条
fsck -V /dev/sdb1 # 详细输出
返回码含义:0 无错误;1 已修复错误;2 需要重启;4 仍有未修复错误;8 操作错误。
e2fsck(ext2/3/4 专用,CentOS 6 主力)
e2fsck /dev/sdb1 # 交互式检查
e2fsck -f /dev/sdb1 # 强制检查(缩容前必做)
e2fsck -f -y /dev/sdb1 # 强制检查并自动 yes(最常用组合)
e2fsck -p /dev/sdb1 # 自动修复安全的问题(preen 模式,开机自检用)
e2fsck -c /dev/sdb1 # 同时检查坏道并记入坏块列表
e2fsck -n /dev/sdb1 # 只读检查,不做任何修改
e2fsck -v /dev/sdb1 # 显示详细统计
# 超级块损坏时用备份超级块修复
mkfs.ext4 -n /dev/sdb1 # 先查出备份超级块位置(-n 不写盘)
dumpe2fs /dev/sdb1 | grep -i superblock
e2fsck -b 32768 /dev/sdb1 # 用位于 32768 块的备份超级块修复
e2fsck -b 32768 -B 4096 /dev/sdb1 # 同时指定块大小
xfs_repair(xfs 专用,CentOS 7 主力)
xfs 没有 fsck 的完整实现(fsck.xfs 是个什么都不做的空脚本),必须使用 xfs_repair:
umount /data # 必须先卸载
xfs_repair /dev/sdb1 # 检查并修复
xfs_repair -n /dev/sdb1 # 只检查不修复(dry run)
xfs_repair -L /dev/sdb1 # 清空日志再修复(!!! 最后手段,会丢失未提交的数据 !!!)
xfs_repair -v /dev/sdb1 # 详细输出
xfs_repair -m 2048 /dev/sdb1 # 限制使用的内存(MB),大文件系统修复时可能需要
xfs_repair -d /dev/sdb1 # 允许修复以只读方式挂载的(危险,仅单用户模式)
典型场景:xfs 因异常断电挂载失败,提示 Structure needs cleaning:
mount /dev/sdb1 /data
# mount: Structure needs cleaning
xfs_repair /dev/sdb1
# 若提示 "log dirty, mount the filesystem to replay the log",先尝试挂载让日志重放:
mount /dev/sdb1 /data && umount /data
# 若挂载不上,最后手段:
xfs_repair -L /dev/sdb1
mount /dev/sdb1 /data
lost+found 目录
-
lost+found是 ext 系列文件系统格式化时自动创建在根目录下的特殊目录(xfs 没有)。 -
fsck/e2fsck修复时,如果找到"有 inode 和数据块但目录项丢失"的孤儿文件(即数据还在但不知道原来叫什么名字、在哪个目录),就会把它们以 inode 号为文件名放入lost+found。 -
修复后应检查该目录:
ls -l /lost+found/ file /lost+found/#12345 # 判断文件类型 head /lost+found/#12345 # 查看内容以确认是什么文件 mv /lost+found/#12345 /data/recovered.conf # 人工辨认后归位 -
不要删除
lost+found目录本身;若误删可用mklost+found重建。
3.10 调整文件系统大小
resize2fs(ext2/3/4)
# 扩容(可在线,文件系统挂载着也能扩)
resize2fs /dev/vg0/lv0 # 扩到设备/分区的最大可用大小(最常用)
resize2fs /dev/vg0/lv0 20G # 指定扩到 20G
resize2fs -p /dev/vg0/lv0 # 显示进度
# 缩容(必须先卸载 + 强制检查)
umount /data
e2fsck -f /dev/vg0/lv0 # 缩容前强制检查,必做
resize2fs /dev/vg0/lv0 10G # 把文件系统缩到 10G
mount /dev/vg0/lv0 /data
# 其它
resize2fs -M /dev/sdb1 # 缩到最小可能尺寸
resize2fs -P /dev/sdb1 # 打印最小可能尺寸(不修改)
resize2fs -f /dev/sdb1 # 强制执行(跳过某些安全检查)
要点:
- 扩容支持在线,缩容必须卸载;
- 缩容目标不能小于已用数据量;
- 缩容前必须
e2fsck -f,否则 resize2fs 会拒绝执行; resize2fs只调整文件系统,不会调整分区或逻辑卷的大小。
xfs_growfs(xfs,只能扩不能缩)
xfs_growfs /data # 扩到底层设备的最大可用大小(参数是挂载点!)
xfs_growfs -D 5242880 /data # 指定扩到 5242880 个块
xfs_growfs -d /data # 只扩数据区(默认行为)
xfs_growfs -n /data # 只显示当前几何信息,不做修改
xfs_growfs -m 30 /data # 调整 inode 最大占比 imaxpct
要点:
- xfs 必须在挂载状态下扩容,且参数传的是挂载点(不是设备名),这与 resize2fs 相反;
- xfs 不支持缩容,这是设计上的限制,没有任何工具能缩小 xfs。需要缩小只能:备份数据 → 重建更小的文件系统 → 恢复数据。
对比记忆:
| 操作 | ext4 | xfs |
|---|---|---|
| 扩容命令 | resize2fs 设备 |
xfs_growfs 挂载点 |
| 扩容是否需卸载 | 不需要(在线可扩) | 必须挂载着 |
| 缩容 | 支持(须卸载 + e2fsck -f) | 不支持 |
| 一步完成(LVM) | lvextend -r -L +10G /dev/vg/lv |
lvextend -r -L +10G /dev/vg/lv(内部自动调用 xfs_growfs) |
tune2fs(调整 ext 文件系统参数)
tune2fs -l /dev/sdb1 # 列出超级块中的所有参数(最常用)
tune2fs -L mydata /dev/sdb1 # 修改卷标(等价 e2label)
tune2fs -U random /dev/sdb1 # 修改 UUID
tune2fs -m 1 /dev/sdb1 # 把 root 保留块从 5% 改为 1%(大盘能省出几十 GB)
tune2fs -r 10000 /dev/sdb1 # 直接指定保留块数量
tune2fs -c 30 /dev/sdb1 # 每挂载 30 次后强制自检
tune2fs -c -1 /dev/sdb1 # 关闭基于挂载次数的自检(生产常用,避免开机长时间等待)
tune2fs -i 30d /dev/sdb1 # 每 30 天强制自检;-i 0 关闭时间检查
tune2fs -C 0 /dev/sdb1 # 重置挂载计数
tune2fs -j /dev/sdb1 # 给 ext2 添加日志,升级为 ext3
tune2fs -O ^has_journal /dev/sdb1 # 关闭日志(^ 表示取消特性)
tune2fs -O has_journal /dev/sdb1 # 重新开启日志
tune2fs -e remount-ro /dev/sdb1 # 设置出错行为:continue / remount-ro / panic
tune2fs -o journal_data_writeback /dev/sdb1 # 设置默认挂载选项
dumpe2fs / xfs_info(查看文件系统详细信息)
# ext 系列
dumpe2fs /dev/sdb1 # 显示超级块 + 所有块组信息(输出很长)
dumpe2fs -h /dev/sdb1 # 只看超级块信息(-h = header,最常用)
dumpe2fs -b /dev/sdb1 # 列出坏块
dumpe2fs -h /dev/sdb1 | grep -E 'Block size|Inode count|Free|Filesystem state'
关键字段含义:Filesystem state(clean/not clean)、Block count(总块数)、Block size(块大小)、Inode count(inode 总数)、Free blocks/inodes(空闲量)、Reserved block count(保留块)、Mount count / Maximum mount count(挂载次数/上限)、Last checked / Check interval。
# xfs 系列
xfs_info /data # 查看 xfs 详细几何信息(参数是挂载点)
xfs_info /dev/sdb1 # 也可传设备(需未挂载或 CentOS 7.3+)
xfs_admin -l /dev/sdb1 # 查看卷标
xfs_admin -u /dev/sdb1 # 查看 UUID
xfs_admin -L newlabel /dev/sdb1 # 修改卷标(须卸载)
xfs_admin -U generate /dev/sdb1 # 重新生成 UUID(克隆虚拟机后必做)
xfs_db -r /dev/sdb1 # 只读方式进入 xfs 调试器
xfs_bmap -v /data/bigfile # 查看文件的块分布(观察碎片)
xfs_fsr /data # 在线碎片整理(reorganize)
xfs_fsr -v /dev/sdb1 # 详细模式
filefrag -v /data/bigfile # 通用的碎片查看工具(ext4/xfs 都支持)
xfs_info 输出解读:meta-data 段的 isize(inode 大小)、agcount(分配组数)、data 段的 bsize(块大小)、blocks(总块数)、naming 段的 version、log 段的日志大小、realtime 段。
3.11 文件系统级备份工具
ext 系列:dump / restore
yum install dump -y
dump -0uj -f /backup/data.dump.bz2 /data # 0 级(全量)备份,-u 更新 /etc/dumpdates,-j bzip2 压缩
dump -1uj -f /backup/data.inc1.bz2 /data # 1 级(增量)备份,只备份自 0 级以来的变化
dump -W # 查看各文件系统的备份级别与时间
dump -0u -f /dev/st0 /home # 备份到磁带机
restore -t -f /backup/data.dump.bz2 # 查看备份内容(-t 列表)
restore -C -f /backup/data.dump.bz2 # 比较备份与当前文件系统的差异
restore -i -f /backup/data.dump.bz2 # 交互式恢复(add/extract 选择性恢复)
restore -r -f /backup/data.dump.bz2 # 完整恢复到当前目录(需先在空文件系统中执行)
要点:dump 支持 0~9 共 10 个备份级别(0 为全量,1~9 为增量),只能备份整个 ext 文件系统(对目录只支持 0 级)。
xfs 系列:xfsdump / xfsrestore
yum install xfsdump -y
xfsdump -l 0 -L "full_20260822" -M "media0" -f /backup/data.xfsdump /data
# -l 备份级别(0 全量,1~9 增量)
# -L 会话标签(session label)
# -M 介质标签(media label)
# -f 输出文件或磁带设备
xfsdump -l 1 -L "inc1" -M "media0" -f /backup/data.inc1 /data # 增量备份
xfsdump -I # 查看备份历史(inventory,记录在 /var/lib/xfsdump)
xfsdump -J -l 0 -f /backup/x.dump /data # -J 不更新 inventory
xfsdump -e -l 0 -f - /data | ssh remote 'cat > /backup/x.dump' # 通过管道远程备份
xfsrestore -f /backup/data.xfsdump /data # 恢复到 /data
xfsrestore -f /backup/data.xfsdump -i /data # 交互式恢复
xfsrestore -t -f /backup/data.xfsdump # 只查看内容
xfsrestore -r -f /backup/data.inc1 /data # 累积恢复增量备份(先恢复 0 级再按顺序恢复增量)
要点:xfsdump 只能备份已挂载的 xfs 文件系统,且必须以 root 运行;备份 inventory 存于 /var/lib/xfsdump/inventory,迁移机器时应一并保留。
备份策略、
tar/rsync/快照备份、异地备份与恢复演练> 详见 03-03-备份与恢复.md
四、swap 交换分区管理
swap 交换分区在物理内存不足时充当虚拟内存使用。当物理内存不够用时,内核会把不活跃的内存页换出到 swap 设备。swap 分区没有文件系统。
4.1 查看 swap 使用情况
free -m # 以 MB 为单位显示内存及 swap 使用情况
free -h # 自动选择易读单位
free -g # 以 GB 为单位
swapon -s # 列出所有已启用的 swap 设备及使用量
cat /proc/swaps # 等效于 swapon -s
swapon --show # CentOS 7 新格式输出
cat /proc/meminfo | grep -i swap # 查看 swap 总量与空闲量
4.2 swap 相关命令
| 命令 | 作用 |
|---|---|
mkswap |
将设备或文件格式化为 swap(写入 swap 签名) |
swapon |
启用 swap |
swapoff |
关闭 swap |
free -m |
查看内存与 swap 使用量 |
mkswap 支持两种载体:
mkswap /dev/sdc1 # 使用一个分区作为 swap
mkswap /swapfile # 使用一个文件作为 swap
mkswap -L myswap /dev/sdc1 # 指定卷标
mkswap -c /dev/sdc1 # 格式化前检查坏块
4.3 使用分区作为 swap(完整演练)
# 1. 用 fdisk 新建一个分区(例如 /dev/sdc1,大小 1G),并把类型改为 82
fdisk /dev/sdc
# n → p → 1 → 回车 → +1G → t → 82 → w
partprobe /dev/sdc
# 2. 格式化为 swap
mkswap /dev/sdc1
# 3. 启用
swapon /dev/sdc1
swapon -p 10 /dev/sdc1 # -p 指定优先级(数值越大越优先使用,-1~32767)
# 4. 查看 swap 容量是否增加
free -m
swapon -s
# 5. 写入 /etc/fstab 实现开机自动启用
blkid /dev/sdc1 # 取 UUID
echo 'UUID=xxxx-xxxx swap swap defaults 0 0' >> /etc/fstab
swapon -a # 启用 fstab 中所有 swap,验证配置
# 6. 关闭 swap
swapoff /dev/sdc1 # 取消 sdc1 作为 swap
free -m # 确认容量已缩减
swapoff -a # 关闭所有 swap
注意:
swapoff会把 swap 中的数据全部换回物理内存,若物理内存不足会失败甚至触发 OOM,生产环境操作前先确认free -m有足够空闲内存。
4.4 使用文件作为 swap(完整演练)
当不便重新分区时,可使用 dd 生成一个二进制大文件作为 swap:
# 1. 创建 1GB 的文件
dd if=/dev/zero of=/swapfile bs=1M count=1024
# if=/dev/zero 输入源为零设备
# of=/swapfile 输出文件路径
# bs=1M 每次写入 1M
# count=1024 写入 1024 次,总计 1024M(1G)
# 等效写法
head -c 1G /dev/zero > /swapfile
dd if=/dev/zero of=./swapfile bs=1024 count=1024 # 注意这个只有 1MB(1024×1024B)
fallocate -l 1G /swapfile # 注意:CentOS 7 的 swap 不支持稀疏文件,建议仍用 dd
# 2. 设置权限(否则 swapon 会告警 insecure permissions)
chmod 600 /swapfile
# 3. 格式化并启用
mkswap /swapfile
swapon /swapfile
free -m
# 4. 写入 fstab
echo '/swapfile swap swap defaults 0 0' >> /etc/fstab
# 5. 关闭并删除
swapoff /swapfile
sed -i '\#^/swapfile#d' /etc/fstab
rm -f /swapfile
该文件是二进制文件,无法使用
cat或vim正常查看和编辑。
4.5 swap 容量规划与 swappiness
经验规划:
| 物理内存 | 建议 swap 大小 |
|---|---|
| ≤ 2 GB | 内存的 2 倍 |
| 2 ~ 8 GB | 与内存相同 |
| 8 ~ 64 GB | 4 GB ~ 内存的 0.5 倍 |
| > 64 GB | 4 GB 即可(或按休眠需求 = 内存大小) |
| 数据库/Redis 等 | 保留少量 swap 防 OOM,但通过 swappiness 降低使用倾向 |
cat /proc/sys/vm/swappiness # 查看当前值(CentOS 6 默认 60,CentOS 7 默认 30)
sysctl vm.swappiness=10 # 临时调整(0~100,越小越倾向于用物理内存)
echo 'vm.swappiness = 10' >> /etc/sysctl.conf # 永久生效
sysctl -p
内存与 swap 的性能调优
> 详见 04-03-Linux系统优化.md
五、LVM 逻辑卷管理
5.1 LVM 的意义与解决的问题
LVM(Logical Volume Manager,逻辑卷管理器)是 Linux 下对磁盘空间进行动态管理(扩容/缩减)的方法,在物理磁盘/分区之上建立一个抽象层。
传统分区方式的痛点与 LVM 的对应解决:
| 传统分区的问题 | LVM 的解决方案 |
|---|---|
分区大小在创建时就固定,/data 满了无法扩大 |
动态扩容:加块硬盘就能给现有文件系统扩容,业务不中断 |
| 单个文件系统不能超过单块磁盘的容量 | 跨磁盘:多块磁盘组成一个卷组,逻辑卷可横跨多块盘 |
| 扩容必须停机、备份、重新分区、恢复数据 | 在线扩容:lvextend -r 一条命令,无需卸载(ext4/xfs 都支持在线扩) |
| 备份大文件系统时数据在变化,无法保证一致性 | 快照(Snapshot):瞬间创建某一时刻的一致性视图,基于快照做备份 |
| 空间预估错误导致某分区浪费、某分区不足 | 灵活再分配:从 ext4 卷缩容,把空间给别的卷(xfs 不能缩) |
| 更换磁盘需要停机迁移数据 | 在线迁移:pvmove 把数据从旧盘搬到新盘,业务无感知 |
| 无法做条带化提升性能 | 条带化/镜像:lvcreate -i(条带)/-m(镜像) |
安装:
yum install lvm2 -y # CentOS 6/7 通用
rpm -qa | grep lvm2 # 确认已安装
systemctl status lvm2-lvmetad # CentOS 7 的 LVM 元数据缓存服务
5.2 三层结构:PV / VG / LV / PE
| 层级 | 英文 | 说明 |
|---|---|---|
| 物理卷 PV | Physical Volume | 对应硬盘分区或整块硬盘(也可以是 RAID 阵列 /dev/md0、多路径设备)。创建前建议把分区类型 ID 标记为 8e |
| 物理区域 PE | Physical Extent | PV 被划分成的等大小块,是 LVM 分配空间的最小单位,默认 4 MiB。一个 VG 内所有 PV 的 PE 大小必须相同 |
| 卷组 VG | Volume Group | 由一个或多个 PV 组成的存储池,用于统一管理物理空间。VG 的容量 = 所有 PV 容量之和 |
| 逻辑卷 LV | Logical Volume | 在 VG 之上划分出的"虚拟分区",由若干 PE 组成,可格式化、挂载使用,可动态调整大小 |
| 逻辑区域 LE | Logical Extent | LV 中的逻辑块,与 PE 一一对应(线性模式下) |
关系图:
物理层 /dev/sdb1 /dev/sdc1 /dev/sdd(整盘)
│ │ │
pvcreate pvcreate pvcreate
▼ ▼ ▼
PV 层 [PV1] [PV2] [PV3]
PE PE PE PE PE PE PE PE PE ← 每个 PV 被切成等大的 PE(默认 4MiB)
└────────────┴────────────┘
vgcreate
▼
VG 层 ┌────────── vgdata ──────────┐ ← 一个大的 PE 池
│ PE PE PE PE PE PE PE PE ... │
└──────────────┬───────────────┘
lvcreate
┌───────────┬─┴──────────┬────────────┐
▼ ▼ ▼ ▼
LV 层 [lv_app] [lv_data] [lv_log] (剩余空闲 PE)
20 GB 50 GB 10 GB 可随时分配
│ │ │
mkfs.xfs mkfs.ext4 mkfs.xfs
▼ ▼ ▼
文件系统 /app /data /var/log
一句话流程:
磁盘/分区 --pvcreate--> 物理卷 PV --vgcreate--> 卷组 VG --lvcreate--> 逻辑卷 LV --mkfs/mount--> 文件系统
整体流程描述:把硬盘分区或整块硬盘标记为 PV → 创建 VG 并将 PV 加入 → 在 VG 上划分 LV → 格式化 LV → 挂载使用。
5.3 完整实操流程
步骤 0:准备物理分区并设置类型 8e
# 方式一:分区后作为 PV(推荐,便于识别用途)
fdisk /dev/sdb
# n → p → 1 → 回车 → 回车(用满整盘)
# t → 8e (改为 Linux LVM)
# p 确认 → w 保存
partprobe /dev/sdb
fdisk -l /dev/sdb | grep sdb1 # 确认 Id 为 8e,System 为 Linux LVM
# GPT 磁盘用 parted 设置 lvm 标志
parted -s /dev/sdb mklabel gpt mkpart primary 1MiB 100% set 1 lvm on
# 方式二:不分区,直接把整块裸盘做 PV(云主机数据盘常用,扩容更方便)
pvcreate /dev/sdb
说明:
8e只是"用途声明",不设置也能pvcreate;但设置了它,fdisk -l就能一眼看出这块分区归 LVM 使用,避免误格式化。
步骤 1:创建与管理物理卷(PV)
pvcreate /dev/sdb1 # 将分区初始化为物理卷
pvcreate /dev/sdb1 /dev/sdc1 # 一次初始化多个
pvcreate /dev/sdb # 使用整块裸盘
pvcreate /dev/md0 # 使用 RAID 阵列作为 PV
pvcreate -ff /dev/sdb1 # 强制创建(清除已有 LVM 标记时使用)
pvcreate --dataalignment 1m /dev/sdb1 # 指定数据对齐,RAID/SSD 上优化性能
pvs # 查看 PV 简要信息(最常用)
pvs -o +pv_used # 附加显示已用空间
pvs --units g # 以 GB 为单位显示
pvscan # 扫描系统中所有 PV(稍详细)
pvscan --cache # 刷新 LVM 元数据缓存
pvdisplay # 查看所有 PV 详细信息
pvdisplay /dev/sdb1 # 查看指定 PV 的详细属性(大小、PE 数、所属 VG)
pvdisplay -m /dev/sdb1 # 显示 PE 的映射segment分布
pvchange -x n /dev/sdb1 # 禁止在该 PV 上分配 PE(准备移除该盘时使用)
pvchange -x y /dev/sdb1 # 重新允许分配
pvresize /dev/sdb1 # 底层分区/磁盘扩容后,让 PV 识别新容量
pvck /dev/sdb1 # 检查 PV 元数据一致性
pvremove /dev/sdb1 # 删除 PV 标记(必须先从 VG 中移出)
pvremove -ff /dev/sdb1 # 强制删除
lvmdiskscan # 检测系统中所有硬盘和分区,查找可用于 LVM 的设备
pvs 输出示例与各列含义:
PV VG Fmt Attr PSize PFree
/dev/sdb1 vgdata lvm2 a-- <5.00g 1.00g
/dev/sdc1 vgdata lvm2 a-- <5.00g 5.00g
PV(设备)、VG(所属卷组,空白表示未加入任何 VG)、Fmt(元数据格式 lvm2)、Attr(属性,a = allocatable 可分配)、PSize(总容量)、PFree(剩余可分配容量)。
步骤 2:创建与管理卷组(VG)
vgcreate vgdata /dev/sdb1 # 用一个 PV 创建卷组
vgcreate vgdata /dev/sdb1 /dev/sdc1 # 一次加入多个 PV
vgcreate myvg /dev/hdb1 # 老式 IDE 盘写法
vgcreate -s 16M vgdata /dev/sdb1 # -s 指定 PE 大小为 16MiB(默认 4MiB)
vgcreate -s 32M -p 128 vgdata /dev/sdb1 # -p 限制最大 PV 数量
vgs # 查看 VG 简要信息(最常用)
vgs --units g # 指定单位
vgscan # 扫描系统中所有卷组
vgdisplay # 查看所有 VG 详细信息
vgdisplay vgdata # 查看指定卷组(PE 大小、总 PE、空闲 PE)
vgdisplay -v vgdata # 同时显示其中的 PV 和 LV
vgextend vgdata /dev/sdd1 # 向卷组添加新 PV,实现卷组在线扩容
vgextend myvg /dev/hdb2
vgreduce vgdata /dev/sdd1 # 把未使用的 PV 移出卷组(缩小卷组)
vgreduce --removemissing vgdata # 移除已丢失/损坏的 PV(磁盘物理损坏后救急)
vgrename vgdata vgnew # 重命名卷组(须处于非活动状态/未挂载)
vgrename <VG-UUID> vgnew # 用 UUID 重命名(VG 名冲突时使用)
vgremove vgdata # 删除卷组(必须先删除其中所有 LV)
vgremove -f vgdata # 强制删除
vgchange -a y vgdata # 激活卷组(使其 LV 可用)
vgchange -a n vgdata # 停用卷组
vgck vgdata # 检测卷组 VGDA(卷组描述符区域)的一致性
vgmerge vgnew vgold # 合并两个已存在的卷组
vgsplit vgdata vgnew /dev/sdc1 # 把一个卷组拆分为两个
vgmknodes # 重新建立卷组目录与其中的设备文件节点
vgconvert -M2 vgdata # 元数据格式从 LVM1 转 LVM2
vgdisplay 关键字段:
--- Volume group ---
VG Name vgdata
VG Size 9.99 GiB <- 卷组总容量
PE Size 4.00 MiB <- 每个 PE 的大小
Total PE 2558 <- PE 总数
Alloc PE / Size 1280 / 5.00 GiB <- 已分配
Free PE / Size 1278 / 4.99 GiB <- 剩余可分配(扩容前必看这一行)
VG UUID xxxx-xxxx
关于 PE 大小的选择:
- 默认 4 MiB,兼容性最好;
- PE 大小决定单个 LV 的理论上限(LVM2 中已不再是硬限制,但影响元数据规模);
- 超大卷组(几十 TB)可用
-s 32M或-s 64M减少 PE 数量、提升管理效率; - PE 大小在 VG 创建后无法直接修改(需要删除重建 VG),务必一开始规划好。
步骤 3:创建与管理逻辑卷(LV)
逻辑卷从卷组中划分空间,对用户而言相当于一块可灵活调整大小的"虚拟磁盘"。
# -L 按容量指定(K/M/G/T)
lvcreate -L 2G -n mylv1 myvg # 在 myvg 中创建 2G 的 mylv1
lvcreate -L 10G -n lvdata vgdata
lvcreate --name lvhome --size 10G vgdata # 长选项写法,等价
# -l 按 PE 数量或百分比指定
lvcreate -l 100 -n lv1 vgdata # 用 100 个 PE(100×4M = 400M)
lvcreate -l 100%FREE -n lvall vgdata # 用满卷组当前所有剩余空间(最常用)
lvcreate -l 50%FREE -n lvhalf vgdata # 用掉剩余空间的一半
lvcreate -l 80%VG -n lvbig vgdata # 用卷组总容量的 80%
lvcreate -l 100%PVS -n lvpv vgdata /dev/sdb1 # 用满指定 PV 的空间
# 指定在哪个 PV 上分配(控制数据落盘位置)
lvcreate -L 5G -n lvfast vgdata /dev/sdb1
# 条带化与镜像
lvcreate -L 10G -i 2 -I 64 -n lvstripe vgdata # -i 条带数,-I 条带大小(KB)
lvcreate -L 10G -m 1 -n lvmirror vgdata # -m 1 创建 1 份镜像
# 快照
lvcreate -s -L 2G -n lvdata_snap /dev/vgdata/lvdata
# 查看
lvs # 查看所有 LV 简要信息(最常用)
lvs -o +devices # 附加显示 LV 落在哪些 PV 上
lvs -a # 显示所有内部卷(含快照元数据、镜像子卷)
lvs --units g
lvscan # 扫描并列出所有逻辑卷
lvdisplay # 查看所有 LV 详细信息
lvdisplay /dev/vgdata/lvdata # 查看指定 LV
lvdisplay -m /dev/vgdata/lvdata # 显示 LE→PE 的映射关系
# 修改与删除
lvrename vgdata lvold lvnew # 重命名(须非活动状态)
lvrename /dev/vgdata/lvold /dev/vgdata/lvnew # 等效写法
lvchange -a n /dev/vgdata/lvdata # 停用逻辑卷
lvchange -a y /dev/vgdata/lvdata # 激活逻辑卷
lvchange -p r /dev/vgdata/lvdata # 设为只读(rw 为读写)
lvremove /dev/vgdata/lvdata # 删除逻辑卷(须先卸载)
lvremove -f /dev/vgdata/lvdata # 强制删除,不提示确认
lvmchange # 紧急情况下复位逻辑卷管理器(极少使用)
lvmdiskscan # 列出所有可用于 LVM 的块设备
lvmsadc # 收集逻辑卷读写统计数据
lvmsar # 报告 lvmsadc 收集到的统计数据
lvs 输出示例:
LV VG Attr LSize Pool Origin Data% Meta% Move Log Cpy%Sync
lvdata vgdata -wi-ao---- 10.00g
lvsnap vgdata swi-a-s-- 2.00g lvdata 12.34
Attr 各位含义(第 1 位类型:- 普通、s 快照、o 原始卷、m 镜像、t thin 池、V thin 卷;第 2 位权限 w/r;第 6 位状态 a 表示 active)。
步骤 4:格式化并挂载 LV
LV 设备路径有两种等价写法:
/dev/<卷组名>/<逻辑卷名> 如 /dev/vgdata/lvdata、/dev/centos/home
/dev/mapper/<卷组名>-<逻辑卷名> 如 /dev/mapper/vgdata-lvdata、/dev/mapper/centos-home
# 格式化
mkfs.xfs /dev/vgdata/lvdata # CentOS 7 推荐
mkfs.ext4 /dev/vgdata/lvdata # CentOS 6 推荐
mkfs.ext4 /dev/myvg/mylv1
mkfs -t ext4 /dev/mapper/vgdata-lvdata # 等效
# 挂载
mkdir -p /data
mount /dev/vgdata/lvdata /data
mount /dev/myvg/mylv1 /mnt
df -hT /data
lsblk # 可以看到 lvm 类型的层级关系
# 写入 fstab 开机自动挂载
blkid /dev/vgdata/lvdata # 也可以取 UUID 使用
# 三种写法都可以,LV 路径本身就是稳定的(不像 /dev/sdb1 会变)
/dev/vgdata/lvdata /data xfs defaults 0 0
/dev/mapper/vgdata-lvdata /data xfs defaults 0 0
UUID=8f5c1c2a-6b3e-4c7a /data xfs defaults 0 0
mount -a # 验证 fstab(必做)
说明:逻辑卷路径由 VG 名和 LV 名决定,只要不改名就永远不变,因此 fstab 中直接写
/dev/vg/lv是安全的,这与裸分区必须用 UUID 的情况不同。但若可能出现 VG 重名(如挂载别的机器的盘),仍以 UUID 最稳妥。
5.4 在线扩容完整演练
核心原则:逻辑卷的大小不能超过所在卷组的剩余空间。 扩容顺序永远是:先扩 VG(如果 VG 没空间了)→ 再扩 LV → 最后扩文件系统。
场景一:VG 还有剩余空间,只扩 LV
# 1. 检查卷组剩余空间(Free PE / Size 一行)
vgs
vgdisplay vgdata | grep Free
# 2. 扩容逻辑卷(三种写法)
lvextend -L +10G /dev/vgdata/lvdata # 增加 10G(注意 + 号)
lvextend -L 30G /dev/vgdata/lvdata # 扩到总共 30G(无 + 号是绝对值)
lvextend -l +100%FREE /dev/vgdata/lvdata # 把卷组所有剩余空间都给它
lvextend -l +100 /dev/vgdata/lvdata # 增加 100 个 PE
lvextend -L +100M /dev/myvg/mylv1 # 增加 100M
lvresize -L +10G /dev/centos/home # lvresize 等效(可扩可缩,lvextend 只能扩)
# 3. 确认逻辑卷已变大,但 df 显示的容量还没变(文件系统还没扩)
lvs
df -hT /data
# 4. 扩容文件系统 —— 区分 ext4 与 xfs!
resize2fs /dev/vgdata/lvdata # ext2/3/4:参数是设备名,可在线执行
xfs_growfs /data # xfs:参数是挂载点,必须挂载着
# 5. 确认
df -hT /data
lsblk
场景二:VG 空间不足,先加盘扩 VG 再扩 LV(最常见)
# 1. 加一块新盘(虚拟机热添加后在线扫描)
for h in /sys/class/scsi_host/host*/scan; do echo '- - -' > $h; done
lsblk # 出现 /dev/sdd
# 2. 分区并设置类型 8e(或直接用裸盘)
fdisk /dev/sdd # n → p → 1 → 回车 → 回车 → t → 8e → w
partprobe /dev/sdd
# 3. 创建 PV
pvcreate /dev/sdd1
pvs
# 4. 把新 PV 加入卷组(在线,业务无感知)
vgextend vgdata /dev/sdd1
vgs # VFree 变大了
# 5. 扩容 LV
lvextend -l +100%FREE /dev/vgdata/lvdata
# 6. 扩容文件系统
xfs_growfs /data # xfs
# 或 resize2fs /dev/vgdata/lvdata # ext4
# 7. 验证
df -hT /data
一步完成扩容:lvextend -r / lvresize -r
-r(--resizefs)会在调整逻辑卷后自动调用对应的文件系统调整工具(内部通过 fsadm),自动识别 ext4 调 resize2fs、xfs 调 xfs_growfs:
lvextend -r -L +10G /dev/vgdata/lvdata # 扩 LV 并自动扩文件系统(推荐)
lvextend -r -l +100%FREE /dev/vgdata/lvdata
lvresize -r -L +10G /dev/centos/home # lvresize 同样支持
lvresize -r -L +10G /dev/centos/home # 等效
# 老式两步写法(等价)
lvextend -L +100M /dev/myvg/mylv1 # 1) 扩容逻辑卷
resize2fs /dev/myvg/mylv1 # 2) 同步扩容文件系统
ext4 与 xfs 扩容差异对照
| 步骤 | ext2/3/4 | xfs |
|---|---|---|
| 扩 LV | lvextend -L +10G /dev/vg/lv |
相同 |
| 扩文件系统命令 | resize2fs /dev/vg/lv |
xfs_growfs /挂载点 |
| 参数是什么 | 设备名 | 挂载点 |
| 是否需要挂载 | 挂载或卸载都可以 | 必须处于挂载状态 |
| 一步完成 | lvextend -r -L +10G ... |
lvextend -r -L +10G ...(同样支持) |
| 可否缩容 | 可以(须卸载) | 不可以 |
扩容根分区(/)的完整演练
# 场景:CentOS 7 默认安装,根分区在 /dev/mapper/centos-root(xfs),磁盘已扩容
# 1. 让内核识别磁盘新容量
echo 1 > /sys/class/block/sda/device/rescan
lsblk
# 2. 新建分区使用新增空间(不要动已有分区!)
fdisk /dev/sda # n → p → 3 → 回车 → 回车 → t → 3 → 8e → w
partprobe /dev/sda # 若失败:partx -a --nr 3 /dev/sda,或安排重启
# 3. 创建 PV 并扩 VG
pvcreate /dev/sda3
vgextend centos /dev/sda3
# 4. 扩 LV + 文件系统(根分区可在线扩容,无需重启)
lvextend -r -l +100%FREE /dev/mapper/centos-root
# 5. 验证
df -hT /
5.5 缩容完整演练
关键限制:xfs 文件系统不支持缩容! 这是设计上的限制,没有任何工具能缩小 xfs。CentOS 7 默认使用 xfs,因此在 CentOS 7 上"缩小根分区腾空间给别的卷"这件事做不到,只能:备份数据 → 删除 LV → 重建更小的 LV → 重新格式化 → 恢复数据。
ext4 缩容的严格顺序
卸载文件系统 → e2fsck -f 强制检查 → resize2fs 缩小文件系统 → lvreduce 缩小逻辑卷 → 重新挂载
umount e2fsck -f resize2fs 10G lvreduce -L 10G mount
顺序绝对不能颠倒:必须先缩文件系统,再缩逻辑卷。如果先缩了逻辑卷,文件系统尾部的数据所在的 PE 会被直接回收,导致文件系统结构损坏、数据永久丢失。
完整命令演练(把 20G 的 lvdata 缩到 10G):
# 0. 【必做】先完整备份数据!缩容是高风险操作
tar -czf /backup/data-$(date +%F).tar.gz /data
# 或 xfsdump / rsync 到其他位置
# 1. 确认当前实际使用量,缩容目标必须大于已用空间
df -h /data
# Filesystem Size Used Avail Use% Mounted on
# /dev/mapper/vgdata-lvdata 20G 6.5G 13G 34% /data
# 已用 6.5G,缩到 10G 是安全的
# 2. 卸载文件系统(缩容必须离线!)
umount /data
# 若报 busy:fuser -mv /data,处理占用进程后重试
# 3. 强制检查文件系统(不做这步 resize2fs 会拒绝执行)
e2fsck -f /dev/vgdata/lvdata
# 或 e2fsck -f -y /dev/vgdata/lvdata
# 4. 先缩小【文件系统】到 10G
resize2fs /dev/vgdata/lvdata 10G
# 输出 The filesystem on /dev/vgdata/lvdata is now 2621440 (4k) blocks long.
# 5. 再缩小【逻辑卷】到 10G
lvreduce -L 10G /dev/vgdata/lvdata
# WARNING: Reducing active logical volume to 10.00 GiB.
# THIS MAY DESTROY YOUR DATA (filesystem etc.)
# Do you really want to reduce vgdata/lvdata? [y/n]: y
# 相对缩减写法:
# lvreduce -L -10G /dev/vgdata/lvdata # 减少 10G
# lvreduce -L -100M /dev/myvg/mylv1 # 减少 100M
# lvreduce -l -100 /dev/vgdata/lvdata # 减少 100 个 PE
# 6. 重新挂载并验证
mount /dev/vgdata/lvdata /data
df -hT /data
ls /data # 确认数据完好
# 7. 现在卷组有了空闲空间,可以分配给其它 LV
vgs
lvextend -r -l +100%FREE /dev/vgdata/lvother
一步完成的写法(谨慎使用):
umount /data
e2fsck -f /dev/vgdata/lvdata
lvreduce -r -L 10G /dev/vgdata/lvdata # -r 自动先缩文件系统再缩 LV
lvresize -r -L -10G /dev/centos/mnt # lvresize 等效写法
mount /dev/vgdata/lvdata /data
-r选项会自动按正确顺序操作,比手工分步更安全,但仍必须先卸载并备份。
缩容注意事项与常见错误
| 错误做法 | 后果 |
|---|---|
先 lvreduce 再 resize2fs |
文件系统损坏、数据永久丢失(尾部数据所在 PE 已被回收) |
不卸载就 resize2fs 缩容 |
报错拒绝执行(幸运);强制执行则文件系统损坏 |
不做 e2fsck -f |
resize2fs 报 Please run 'e2fsck -f' first 拒绝执行 |
| 缩容目标小于已用空间 | resize2fs 报 New size smaller than minimum 拒绝执行 |
| 对 xfs 执行缩容 | 没有任何工具能做到,lvreduce -r 会报错;若强行 lvreduce 则文件系统损坏 |
| 缩容前不备份 | 一旦出错无法挽回 |
查询 ext4 文件系统能缩到的最小尺寸:
resize2fs -P /dev/vgdata/lvdata # 打印最小块数
tune2fs -l /dev/vgdata/lvdata | grep -E 'Block count|Free blocks|Block size'
5.6 LVM 快照(Snapshot)
原理:写时复制(COW,Copy-On-Write)
- 创建快照时不复制任何数据,只是建立一个指向原卷(origin)当前状态的元数据映射,因此瞬间完成。
- 之后原卷上某个块第一次被修改前,LVM 会先把该块的原始内容复制到快照空间,再让写入落到原卷。
- 读快照时:未被修改过的块直接读原卷,被修改过的块读快照空间里保存的旧副本。
- 因此快照看到的永远是创建瞬间的一致性视图。
- 快照空间只需要能容纳"快照存续期间原卷被修改的数据量",通常远小于原卷(经验值:原卷的 10%~20%,写入频繁的库要更大)。
创建与使用快照
# 创建快照(-s 表示 snapshot,最后的参数是原卷路径)
lvcreate -s -L 2G -n lvdata_snap /dev/vgdata/lvdata
lvcreate --snapshot --size 2G --name lvdata_snap /dev/vgdata/lvdata # 长选项
lvcreate -s -l 20%ORIGIN -n lvdata_snap /dev/vgdata/lvdata # 按原卷比例
lvcreate -s -L 5G -n snap_root /dev/mapper/centos-root # 给根卷做快照
# 查看快照(Origin 列显示原卷,Data% 显示快照空间已用比例)
lvs
# LV VG Attr LSize Origin Data%
# lvdata vgdata owi-aos-- 20.00g
# lvdata_snap vgdata swi-a-s-- 2.00g lvdata 3.21
lvdisplay /dev/vgdata/lvdata_snap
lvs -o lv_name,origin,data_percent,lv_size
# 挂载快照读取数据(快照是独立的块设备)
mkdir -p /mnt/snap
mount -o ro /dev/vgdata/lvdata_snap /mnt/snap # ext4:只读挂载即可
mount -o ro,nouuid /dev/vgdata/snap_xfs /mnt/snap # xfs:必须加 nouuid(UUID 与原卷相同会拒绝挂载)
ls /mnt/snap # 看到的是快照创建瞬间的数据
# 删除快照(用完立刻删除,避免持续拖慢原卷性能)
umount /mnt/snap
lvremove /dev/vgdata/lvdata_snap
lvremove -f /dev/vgdata/lvdata_snap
用快照做一致性备份的完整流程
问题背景:直接 tar 一个正在被数据库写入的目录,备份出来的文件可能处于中间状态,恢复后数据不一致。快照解决了这个问题。
# 1.(数据库场景)让数据落盘并短暂加锁,保证快照点一致
mysql -e "FLUSH TABLES WITH READ LOCK;" & # 保持会话持锁
sync # 刷新文件系统缓存
# xfs 还可以冻结文件系统,保证元数据一致:
xfs_freeze -f /data
# 2. 创建快照(秒级完成,业务几乎无感知)
lvcreate -s -L 5G -n snap_data /dev/vgdata/lvdata
# 3. 立即解锁/解冻,业务恢复正常读写
xfs_freeze -u /data
# 结束持锁的 mysql 会话(UNLOCK TABLES)
# 4. 挂载快照
mkdir -p /mnt/snap
mount -o ro,nouuid /dev/vgdata/snap_data /mnt/snap # xfs
# mount -o ro /dev/vgdata/snap_data /mnt/snap # ext4
# 5. 从快照做备份(此时可以慢慢备份,原卷继续读写互不影响)
tar -czf /backup/data-$(date +%F).tar.gz -C /mnt/snap .
# 或
rsync -a --delete /mnt/snap/ /backup/data/
# 或 xfsdump -l 0 -f /backup/data.xfsdump /mnt/snap
# 或 dump -0uj -f /backup/data.dump.bz2 /mnt/snap
# 6. 备份完成,卸载并删除快照
umount /mnt/snap
lvremove -f /dev/vgdata/snap_data
# 7. 验证备份可用
tar -tzf /backup/data-$(date +%F).tar.gz | head
快照空间耗尽的后果
- 快照空间被写满(
lvs的Data%达到 100%)后,快照立即失效(Invalid),无法再挂载读取,其中的备份视图彻底丢失; - 快照失效不会损坏原卷数据,原卷继续正常工作(这是唯一的好消息);
- 因此必须监控快照使用率:
lvs -o lv_name,data_percent,origin # 手工检查
watch -n 5 'lvs' # 备份期间持续观察
# 让 LVM 自动扩容快照,避免写满(推荐配置)
vim /etc/lvm/lvm.conf
# snapshot_autoextend_threshold = 70 # 用到 70% 时自动扩容
# snapshot_autoextend_percent = 20 # 每次扩容 20%
systemctl restart lvm2-monitor # CentOS 7 需要 dmeventd 监控服务在运行
# 手工扩容快照
lvextend -L +2G /dev/vgdata/snap_data
合并快照(回滚原卷到快照时刻)
# lvconvert --merge 把快照内容合并回原卷,实现"回滚"
umount /data # 先卸载原卷(根卷无法卸载则见下)
lvconvert --merge /dev/vgdata/snap_data
# 合并完成后快照会被自动删除
mount /dev/vgdata/lvdata /data
ls /data # 数据已回到快照创建时的状态
# 若原卷正在使用(如根卷)无法卸载,合并会被延迟到下次激活时执行
lvconvert --merge /dev/vgdata/snap_root
# Merging of snapshot will occur on next activation of vgdata/lvroot.
reboot # 重启后自动完成合并
典型用途:升级/打补丁前先做快照,出问题就 lvconvert --merge 一键回滚。
快照与备份的关系(重要观念)
| 观点 | 说明 |
|---|---|
| 快照不是备份 | 快照与原卷在同一个卷组、同一批物理磁盘上,磁盘损坏则原卷与快照一起丢失 |
| 快照是"备份的辅助手段" | 它解决的是"备份期间数据一直在变"的一致性问题,备份数据必须落到其它介质/主机 |
| 快照有性能代价 | 快照存在期间,原卷每次首次写入都要多一次读+写(COW),写性能明显下降。用完立刻删除 |
| 快照不能长期保留 | 空间会被逐渐写满而失效;多个快照叠加性能损耗更大 |
| 快照可用于回滚 | lvconvert --merge 适合升级前的短期保险 |
完整的备份策略(全量/增量、异地、恢复演练)
> 详见 03-03-备份与恢复.md
5.7 条带化与镜像
条带化(Striping,类似 RAID 0)
数据被切成条带轮流写到多个 PV 上,多块盘并行读写,性能成倍提升,但没有冗余(任一盘坏则整个 LV 失效)。
# -i 条带数(必须 ≤ VG 中可用 PV 数),-I 条带大小(KB,必须是 2 的幂)
lvcreate -L 10G -i 2 -I 64 -n lvstripe vgdata
lvcreate -L 20G -i 3 -I 128 -n lvfast vgdata /dev/sdb1 /dev/sdc1 /dev/sdd1
lvs -o +stripes,stripe_size,devices # 查看条带配置
lvdisplay -m /dev/vgdata/lvstripe # 查看 LE→PE 的分布,确认跨了多个 PV
要点:
-i N要求 VG 中至少有 N 个不同的 PV,且每个 PV 都要有足够空间;- 条带大小
-I建议与业务 I/O 大小匹配(数据库 64K,大文件顺序读写 256K 以上); - 条带 LV 扩容时必须一次扩满所有条带盘(
lvextend需要每个 PV 都有空间),否则需要用--alloc anywhere; - 默认(不加
-i)是线性/连续分配(linear):先用满第一个 PV 再用第二个,没有性能提升但扩容灵活。
镜像(Mirroring,类似 RAID 1)
# -m 1 表示 1 份镜像(共 2 份数据),需要至少 2 个 PV + 1 个放日志(或用 --mirrorlog core)
lvcreate -L 10G -m 1 -n lvmirror vgdata
lvcreate -L 10G -m 1 --mirrorlog mirrored -n lvmirror vgdata # 镜像日志也做镜像
lvcreate -L 10G -m 1 --mirrorlog core -n lvmirror vgdata # 日志放内存(重启需完整同步)
# 用 RAID1 类型(CentOS 7 推荐,基于 md 的 dm-raid,比老的 dm-mirror 更好)
lvcreate --type raid1 -m 1 -L 10G -n lvraid1 vgdata
lvcreate --type raid5 -i 3 -L 30G -n lvraid5 vgdata
lvcreate --type raid10 -i 2 -m 1 -L 20G -n lvraid10 vgdata
# 查看同步进度
lvs -o +copy_percent,sync_percent
lvs -a -o name,segtype,sync_percent
# 给已有 LV 添加/移除镜像
lvconvert -m 1 /dev/vgdata/lvdata # 添加镜像
lvconvert -m 0 /dev/vgdata/lvdata # 移除镜像(变回单份)
lvconvert --repair /dev/vgdata/lvraid1 # 修复降级的镜像
LVM 内建 RAID 与 mdadm 软 RAID 的关系与选择
| 方案 | 特点 | 适用场景 |
|---|---|---|
| mdadm 软 RAID + LVM 叠加(推荐) | 分层清晰:磁盘 → md 阵列 → PV/VG/LV。RAID 管理成熟、监控工具完善(/proc/mdstat) |
生产环境标准做法,同时获得冗余与灵活扩容 |
LVM 内建 RAID(--type raid1/5/6/10) |
少一层,可对单个 LV 指定不同的 RAID 级别(同一 VG 内混用) | 需要按卷差异化配置冗余;单机灵活实验 |
LVM 条带 -i |
只提性能无冗余 | 底层已有硬件 RAID 保护时的性能优化 |
常见分层顺序:物理磁盘 → md 阵列 → PV/VG/LV → 文件系统。
# md 设备作为 PV 使用(在阵列上叠加 LVM)
pvcreate /dev/md0
vgcreate vgdata /dev/md0
lvcreate --name lvapp --size 100G vgdata
若已有带缓存电池的硬件 RAID 卡,则直接用硬件 RAID 提供的逻辑盘做 PV,不要再叠加软 RAID。RAID 详见本文第七章。
5.8 精简置备(Thin Provisioning)简述
thin provisioning(精简卷/超额分配)允许创建"名义容量大于实际物理容量"的逻辑卷,空间按实际写入量从 thin pool 中动态分配。
# 1. 创建 thin pool(精简池),大小为实际可用物理空间
lvcreate -L 20G --thinpool tp0 vgdata
lvcreate -L 20G --type thin-pool -n tp0 vgdata # 等效写法
lvcreate -l 100%FREE --thinpool tp0 vgdata
# 2. 在池中创建 thin LV,-V 是"虚拟大小",可以超过池的实际容量(超额分配)
lvcreate -V 50G -T vgdata/tp0 -n lvthin1
lvcreate -V 50G --thin vgdata/tp0 -n lvthin2 # 两个卷各"号称"50G,池只有 20G
# 3. 正常格式化挂载使用
mkfs.xfs /dev/vgdata/lvthin1
mount /dev/vgdata/lvthin1 /data1
# 4. 监控池的实际使用率(Data% 和 Meta%)——这是 thin 的生命线
lvs -o lv_name,lv_size,data_percent,metadata_percent
lvs -a # 查看池的内部卷
# 5. 池不够了就扩容池
lvextend -L +10G vgdata/tp0
# 6. thin 卷的快照几乎不占空间(不需要预留固定大小)
lvcreate -s -n snap1 vgdata/lvthin1
优点:空间利用率高、快照极其廉价(可保留多个)、支持 discard 回收空间。
风险(必须理解):一旦 thin pool 被写满,所有基于它的 thin LV 都会立即 I/O 错误、文件系统被置为只读甚至损坏。因此:
# 配置自动扩容(/etc/lvm/lvm.conf)
thin_pool_autoextend_threshold = 70
thin_pool_autoextend_percent = 20
systemctl enable --now lvm2-monitor
生产使用 thin 必须配套容量监控告警,且 VG 中要保留可扩容的空闲空间。
5.9 LVM 常见问题与运维操作
VG 元数据备份与恢复
LVM 会在每次修改元数据时自动备份,无需手工操作:
- 自动备份目录:
/etc/lvm/backup/(当前元数据,每个 VG 一个文件) - 历史归档目录:
/etc/lvm/archive/(每次变更前的旧版本,带序号和时间戳)
# 手工备份元数据
vgcfgbackup # 备份所有 VG 到 /etc/lvm/backup/
vgcfgbackup vgdata # 备份指定 VG
vgcfgbackup -f /backup/vgdata.conf vgdata # 备份到指定文件
# (老文档中写作备份到 /etc/vmconf,现代 LVM2 为 /etc/lvm/backup)
# 查看可用的归档版本
ls -l /etc/lvm/archive/
vgcfgrestore --list vgdata # 列出该 VG 所有可恢复的元数据版本及时间
# 恢复元数据(典型场景:误删了 LV,想恢复元数据把 LV 找回来)
vgcfgrestore -f /etc/lvm/archive/vgdata_00003-123456789.vg vgdata
vgcfgrestore vgdata # 恢复到最近一次备份
vgchange -ay vgdata # 恢复后重新激活
lvs # 确认 LV 回来了
mount /dev/vgdata/lvdata /data # 数据通常完好(只要 PE 未被覆盖写入)
# PV 元数据损坏/UUID 丢失时的修复
pvcreate --uuid <原PV的UUID> --restorefile /etc/lvm/backup/vgdata /dev/sdb1
vgcfgrestore vgdata
重要:误
lvremove后不要再做任何写操作(不要创建新 LV),立即用vgcfgrestore恢复元数据,数据大概率能救回来。
迁移 VG 到新机器(vgexport / vgimport)
# ===== 在源机器上 =====
umount /data # 1. 卸载所有该 VG 上的文件系统
lvchange -an /dev/vgdata/lvdata # 2. 停用逻辑卷
vgchange -an vgdata # 3. 停用卷组
vgexport vgdata # 4. 导出卷组(系统不再识别它)
vgs # 状态显示 exported(Attr 中有 x)
vgcfgbackup vgdata # 5.(建议)备份元数据一起带走
# 6. 关机,把磁盘物理搬到新机器(或在存储上重新映射 LUN)
# ===== 在目标机器上 =====
pvscan # 7. 扫描发现 PV(会提示 is in exported VG)
vgscan # 8. 扫描发现卷组
vgimport vgdata # 9. 导入卷组
vgchange -ay vgdata # 10. 激活卷组
lvs # 11. 查看逻辑卷
mount /dev/vgdata/lvdata /data # 12. 挂载使用
vim /etc/fstab # 13. 写入 fstab
# 若两台机器上 VG 同名冲突,先重命名再导入
vgrename <VG-UUID> vgdata_old
vgs -o vg_name,vg_uuid # 查看 UUID
pvmove:在线迁移数据后移除旧盘
场景:一块旧盘即将故障(SMART 报警)或要换成 SSD,需要在业务不停机的情况下把数据搬走并拔掉旧盘。
# 1. 加入新盘作为新 PV,并扩入同一个卷组
pvcreate /dev/sde1
vgextend vgdata /dev/sde1
vgs # 确认新盘容量已进入 VG,且 VFree ≥ 旧盘已用量
# 2.(建议)禁止在旧盘上再分配新的 PE
pvchange -x n /dev/sdb1
# 3. 在线迁移数据(把 sdb1 上的所有 PE 搬到其它 PV 上)
pvmove /dev/sdb1 # 自动选择目标 PV
pvmove /dev/sdb1 /dev/sde1 # 指定迁移到 sde1
pvmove -v /dev/sdb1 # 详细输出
pvmove -i 10 /dev/sdb1 # 每 10 秒报告一次进度
pvmove -n lvdata /dev/sdb1 /dev/sde1 # 只迁移指定 LV 的数据
# 迁移过程是在线的,业务可继续读写;速度取决于数据量与磁盘性能
# 中断与续传
pvmove --abort # 中止迁移(数据回退,安全)
pvmove # 不带参数:继续之前被中断的迁移
# 4. 查看迁移进度
lvs -o +move_pv,copy_percent
pvs
dmsetup status # 观察 pvmove 镜像进度
# 5. 迁移完成后确认旧盘已空(PFree 等于 PSize)
pvs /dev/sdb1
pvdisplay /dev/sdb1 | grep -E 'Allocated|Free'
# 6. 从卷组中移出旧盘
vgreduce vgdata /dev/sdb1
# 7. 清除旧盘的 PV 标记
pvremove /dev/sdb1
# 8. 现在可以安全地物理拔掉旧盘了(先在系统里删除设备)
echo 1 > /sys/block/sdb/device/delete
磁盘物理损坏后的应急处理
vgdisplay vgdata # 提示 Couldn't find device with uuid ...
vgs # 显示 [unknown] 设备
vgreduce --removemissing vgdata # 移除丢失的 PV(该 PV 上的 LV 会被删除!)
vgreduce --removemissing --force vgdata # 强制(会删除引用了丢失 PV 的 LV)
vgchange -ay --partial vgdata # 部分激活,尝试抢救仍完整的 LV 上的数据
vgcfgrestore vgdata # 换上新盘并恢复元数据后重建
其它常见问题
| 问题 | 原因与解决 |
|---|---|
lvcreate 报 Insufficient free space |
VG 空间不足:vgs 看 VFree,vgextend 加盘;或改用 -l 100%FREE |
| 开机后 LV 未激活、fstab 挂载失败 | vgchange -ay 激活;检查 lvm2-lvmetad/lvm2-monitor 服务;dracut -f 重建 initramfs |
pvcreate 报 Device /dev/sdb1 excluded by a filter |
设备上有旧的文件系统/RAID 签名:wipefs -a /dev/sdb1;或检查 /etc/lvm/lvm.conf 的 filter 配置 |
vgremove 报还有 LV |
先 lvremove 删除所有 LV |
pvremove 报 PV is used by VG |
先 vgreduce 把它移出 VG |
| 克隆虚拟机后两台机器 VG 同名 | vgrename <UUID> newname;xfs 还需 xfs_admin -U generate 换 UUID |
lvs 看不到某个 VG |
pvscan --cache && vgscan && vgchange -ay |
| 想知道某个 LV 的数据落在哪块物理盘 | lvdisplay -m /dev/vg/lv 或 lvs -o +devices |
5.10 附录:LVM 命令速查表
本节完整保留《02-LVM逻辑卷命令速查.md》的速查内容,便于运维时快速查阅。
LVM 三层结构关系:
磁盘/分区 --pvcreate--> 物理卷 PV --vgcreate--> 卷组 VG --lvcreate--> 逻辑卷 LV --mkfs/mount--> 文件系统
附录 A-1:物理卷(PV)管理命令
物理卷是 LVM 的底层存储提供者,通常由整块磁盘或磁盘分区构成。
| 序号 | 命令语法 | 说明 |
|---|---|---|
| 1 | pvcreate /dev/hdb1 |
将指定磁盘或分区初始化为物理卷,供 LVM 使用 |
| 2 | pvscan(或 pvs) |
扫描并列出系统中所有的物理卷信息,也可指定具体设备查看 |
| 3 | pvdisplay /dev/hdb1 |
显示指定物理卷的详细属性信息(大小、PE、所属 VG 等) |
| 4 | pvchange |
改变物理卷的属性(如允许/禁止分配 PE:pvchange -x n /dev/hdb1) |
| 5 | pvmove |
将物理卷上的数据搬移到其他物理卷,常用于移除磁盘前的数据迁移 |
补充说明:
pvs是pvscan的精简输出形式,适合快速浏览;pvdisplay输出更详细。- 若要从系统中移除一块物理卷,应先用
pvmove搬移数据,再用vgreduce将其从卷组中移出,最后用pvremove删除物理卷标记。
附录 A-2:卷组(VG)管理命令
卷组由一个或多个物理卷组成,是物理卷与逻辑卷之间的中间抽象层。
| 序号 | 命令语法 | 说明 |
|---|---|---|
| 1 | vgscan(或 vgs) |
扫描并列出系统中所有的卷组信息,也可指定卷组名查看 |
| 2 | vgcreate myvg /dev/hdb1 |
使用指定物理卷创建名为 myvg 的卷组 |
| 3 | vgextend myvg /dev/hdb2 |
向卷组 myvg 中添加新的物理卷,实现卷组在线扩容 |
| 4 | vgcfgbackup |
备份卷组中各物理卷的 VGDA 信息,默认备份到 /etc/lvm/backup 目录(原文写作 /etc/vmconf) |
| 5 | vgcfgrestore |
从备份文件中恢复卷组中物理卷的 VGDA 信息 |
| 6 | vgchange |
改变卷组属性,常用 -a y/-a n 激活或停用卷组 |
| 7 | vgck |
检测卷组中 VGDA(卷组描述符区域)的一致性 |
| 8 | vgdisplay |
显示卷组的详细信息(PE 大小、总 PE、空闲 PE 等) |
| 9 | vgexport |
导出卷组,使系统不再识别该卷组(常用于迁移到其他机器) |
| 10 | vgimport |
与 vgexport 相反,将导出的卷组重新导入系统使其可被识别 |
| 11 | vgmerge |
合并两个已存在的卷组 |
| 12 | vgmknodes |
重新建立已有卷组的卷组目录和其中的设备文件节点 |
| 13 | vgreduce |
把卷组中未使用的物理卷移出卷组(缩小卷组) |
| 14 | vgremove |
删除空的(不包含任何逻辑卷的)卷组 |
| 15 | vgrename |
重命名非活动状态(未挂载使用)的卷组 |
| 16 | vgsplit |
把一个卷组拆分为两个卷组 |
补充说明:
vgs是精简输出,vgdisplay是详细输出。- 扩容卷组流程:
pvcreate /dev/hdb2→vgextend myvg /dev/hdb2。 - 缩小卷组前,应先用
pvmove把待移除物理卷上的数据搬移走,再执行vgreduce。
附录 A-3:逻辑卷(LV)管理命令
逻辑卷从卷组中划分空间,对用户而言相当于一块可灵活调整大小的"虚拟磁盘",可在其上创建文件系统并挂载使用。
| 序号 | 命令语法 | 说明 |
|---|---|---|
| 1 | lvscan(或 lvs) |
扫描并列出系统中所有逻辑卷,也可指定具体逻辑卷查看 |
| 2 | lvcreate -L 2G -n mylv1 myvg |
在卷组 myvg 中新建名为 mylv1 的逻辑卷。-L 指定实际大小(如 2G),-l 可指定 PE 数量或百分比大小(如 -l +100%FREE) |
| 3 | lvextend -L +100M /dev/myvg/mylv1 |
扩充逻辑卷 mylv1 的容量,增加 100M |
| 4 | lvreduce -L -100M /dev/myvg/mylv1 |
缩小逻辑卷 mylv1 的容量,减少 100M |
| 5 | resize2fs /dev/myvg/mylv1 |
调整 ext2/ext3/ext4 文件系统的容量(适用于已挂载使用的逻辑卷,扩容后同步文件系统) |
| 6 | e2fsadm |
调整未挂载的 ext2/ext3 文件系统对应的逻辑卷容量 |
| 7 | lvremove |
删除非活动状态(未挂载使用)的逻辑卷 |
| 8 | resize_reiserfs |
调整 ReiserFS 文件系统的容量 |
| 9 | lvchange |
改变逻辑卷的属性,如 -a y/-a n 激活或停用逻辑卷 |
| 10 | lvdisplay |
显示逻辑卷的详细属性信息 |
| 11 | lvrename |
对非活动状态的逻辑卷改名 |
| 12 | lvmchange |
在紧急情况下用于复位逻辑卷管理器 |
| 13 | lvmdiskscan |
检测系统中所有硬盘和分区,查找可用于 LVM 的存储设备 |
| 14 | lvmsadc |
收集逻辑卷的读写统计数据(原文写作 lvmsade,应为 lvmsadc) |
| 15 | lvmsar |
报告 lvmsadc 命令收集到的逻辑卷读写统计数据 |
补充说明:
-
lvs是精简输出,lvdisplay是详细输出。 -
逻辑卷设备路径有两种等价写法:
/dev/myvg/mylv1或/dev/mapper/myvg-mylv1。 -
扩容流程(ext 文件系统):
lvextend -L +100M /dev/myvg/mylv1 # 扩容逻辑卷 resize2fs /dev/myvg/mylv1 # 同步扩容文件系统 -
缩容风险提示:缩容逻辑卷前务必备份数据,并先缩小文件系统再缩小逻辑卷,否则可能导致数据损坏。XFS 文件系统不支持缩容。
附录 A-4:典型操作流程速记
# 1. 创建物理卷
pvcreate /dev/hdb1
# 2. 创建卷组
vgcreate myvg /dev/hdb1
# 3. 创建逻辑卷
lvcreate -L 2G -n mylv1 myvg
# 4. 创建文件系统并挂载
mkfs.ext4 /dev/myvg/mylv1
mount /dev/myvg/mylv1 /mnt
# 5. 在线扩容逻辑卷与文件系统
lvextend -L +100M /dev/myvg/mylv1
resize2fs /dev/myvg/mylv1
六、磁盘配额(quota)
6.1 配额的作用与基本概念
作用:限制用户或组在某个文件系统上能使用的磁盘空间和文件数量,防止个别用户写满共享分区(典型场景:多用户共享的 /home、FTP 服务器、邮件服务器、共享开发机)。
关键概念:
| 概念 | 说明 |
|---|---|
| 限制对象 | 用户配额(user quota,按 UID 限制)、组配额(group quota,按 GID 限制)、项目配额(project quota,仅 xfs,按目录限制) |
| 限制内容 | block 配额(限制占用空间大小,单位 KB)、inode 配额(限制文件个数) |
| 软限制 soft | 警告线。超过后仍可继续写入,但进入宽限期倒计时,并在登录时收到警告 |
| 硬限制 hard | 绝对上限。达到后立即禁止继续写入,报 Disk quota exceeded |
| 宽限期 grace period | 超过软限制后允许"超标"的时长(默认 7 天)。宽限期内必须把用量降到软限制以下,否则软限制会变成硬限制,无法再写入 |
重要前提:
- 配额是针对文件系统(分区)生效的,不是针对目录(xfs project quota 除外);
- 要限制
/home,/home必须是独立的分区/逻辑卷; - root 用户不受配额限制。
6.2 ext4 上的配额完整实操(CentOS 6 / CentOS 7 通用)
步骤 0:准备实验环境
# 创建一个独立的文件系统作为实验对象
fdisk /dev/sdb # n → p → 1 → +2G → w
partprobe /dev/sdb
mkfs.ext4 /dev/sdb1
mkdir -p /home2
mount /dev/sdb1 /home2
# 安装配额工具
yum install quota -y
rpm -qa | grep quota
# 创建测试用户
useradd -d /home2/tom tom && echo '123456' | passwd --stdin tom
useradd -d /home2/jerry jerry && echo '123456' | passwd --stdin jerry
groupadd devgrp
usermod -aG devgrp tom
usermod -aG devgrp jerry
chmod 777 /home2
步骤 1:挂载时启用配额选项 usrquota / grpquota
# 临时启用(remount 方式,立即生效)
mount -o remount,usrquota,grpquota /home2
mount | grep home2 # 确认选项中出现 usrquota,grpquota
# 永久启用:写入 /etc/fstab
vim /etc/fstab
UUID=xxxx-xxxx-xxxx /home2 ext4 defaults,usrquota,grpquota 0 0
# 也可写设备名:/dev/sdb1 /home2 ext4 defaults,usrquota,grpquota 0 0
mount -a # 验证 fstab 配置
mount | grep home2 # 再次确认配额选项已生效
# 如果 remount 不生效,则完整卸载再挂载:
umount /home2 && mount /home2
若要对根分区启用配额,加了 fstab 选项后需要重启(或
mount -o remount /)。
步骤 2:quotacheck 创建配额数据库文件
quotacheck -cugv /home2
# -c 创建新的配额数据库文件(create),会覆盖已有的
# -u 处理用户配额,生成 aquota.user
# -g 处理组配额,生成 aquota.group
# -v 显示详细过程
# -a 对 /etc/mtab 中所有启用了配额的文件系统操作
# -m 不要重新挂载为只读(文件系统正在使用时需要)
# -M 强制以读写方式检查
quotacheck -avug # 对所有启用配额的文件系统创建
quotacheck -cugvm /home2 # 文件系统忙时加 -m
ls -l /home2/aquota.*
# -rw------- 1 root root 6144 ... /home2/aquota.group
# -rw------- 1 root root 6144 ... /home2/aquota.user
说明:aquota.user 与 aquota.group 是配额数据库,位于该文件系统的根目录下(CentOS 6/7 均为此名;更老的系统叫 quota.user/quota.group)。
注意:
quotacheck会扫描整个文件系统统计每个用户的用量,大文件系统上耗时较长,且要求文件系统最好处于只读或空闲状态。
步骤 3:quotaon 启用配额
quotaon -avug # 启用所有文件系统的用户与组配额(最常用)
quotaon -v /home2 # 启用指定文件系统
quotaon -u /home2 # 只启用用户配额
quotaon -g /home2 # 只启用组配额
quotaon -p /home2 # 查看配额是否已启用(print state)
# 关闭配额
quotaoff -avug # 关闭所有
quotaoff -v /home2 # 关闭指定文件系统
步骤 4:edquota 设置配额(交互式)
edquota -u tom # 编辑用户 tom 的配额(打开 vi 编辑器)
edquota -g devgrp # 编辑组 devgrp 的配额
打开的编辑界面内容:
Disk quotas for user tom (uid 1001):
Filesystem blocks soft hard inodes soft hard
/dev/sdb1 32 20480 30720 8 80 100
字段含义:
blocks:当前已用空间(KB,由系统统计,不要修改);- 第一组
soft/hard:空间的软/硬限制(KB)。20480= 20MB,30720= 30MB; inodes:当前已用文件数(由系统统计,不要修改);- 第二组
soft/hard:文件数量的软/硬限制; - 数值填
0表示不限制。
保存退出(:wq)即生效。
其它 edquota 用法:
edquota -t # 设置宽限期(对整个文件系统生效,非单个用户)
宽限期编辑界面:
Grace period before enforcing soft limits for users:
Time units may be: days, hours, minutes, or seconds
Filesystem Block grace period Inode grace period
/dev/sdb1 7days 7days
可改为 3days、12hours、30minutes 等。
edquota -p tom jerry # 把 tom 的配额设置复制给 jerry(-p = prototype)
edquota -p tom -u jerry mike lucy # 一次复制给多个用户(批量设置的利器)
for u in u1 u2 u3; do edquota -p tom $u; done # 脚本批量
edquota -u tom -f /dev/sdb1 # 只编辑指定文件系统上的配额
步骤 5:setquota 非交互式设置(脚本首选)
# 语法:setquota -u 用户 块软限制 块硬限制 inode软限制 inode硬限制 文件系统
setquota -u tom 20480 30720 80 100 /home2
setquota -g devgrp 102400 204800 500 600 /home2
# 只限制空间不限制文件数(inode 限制填 0)
setquota -u jerry 51200 102400 0 0 /home2
# 批量为多个用户设置相同配额
for u in tom jerry mike; do setquota -u $u 20480 30720 80 100 /home2; done
# 用 -p 复制原型用户的配额
setquota -p tom -u jerry /home2
# 设置宽限期(-t,单位为秒)
setquota -t 259200 259200 /home2 # 3 天 = 259200 秒
setquota -u tom -T 86400 86400 /home2 # -T 为单个用户设置宽限期
步骤 6:查看配额使用情况
quota # 普通用户查看自己的配额
quota -u tom # 查看指定用户的配额(root 执行)
quota -g devgrp # 查看组配额
quota -uvs tom # -v 详细,-s 以易读单位(M/G)显示
quota -q tom # 只在超限时才输出
repquota -a # 报告所有启用配额的文件系统的配额情况(最常用)
repquota /home2 # 报告指定文件系统
repquota -u /home2 # 只报告用户配额
repquota -g /home2 # 只报告组配额
repquota -as # 所有文件系统 + 易读单位
repquota -av # 详细报告
repquota -a 输出示例:
*** Report for user quotas on device /dev/sdb1
Block grace time: 7days; Inode grace time: 7days
Block limits File limits
User used soft hard grace used soft hard grace
----------------------------------------------------------------------
root -- 20 0 0 2 0 0
tom +- 25600 20480 30720 6days 12 80 100
jerry -- 32 51200 102400 4 0 0
--/+- 标记:第一位是 block 状态,第二位是 inode 状态;- 正常,+ 表示已超过软限制(此时 grace 列显示剩余宽限时间)。
步骤 7:超限现象演练与验证
# 切换到被限制的用户
su - tom
# 1) 写入未超软限制的数据(正常)
dd if=/dev/zero of=/home2/tom/f10m bs=1M count=10
quota -s # 查看用量
# 2) 写入超过软限制(20MB)但未到硬限制(30MB)—— 可以写入,但收到警告
dd if=/dev/zero of=/home2/tom/f15m bs=1M count=15
# sdb1: warning, user block quota exceeded.
quota -s # grace 列开始倒计时(如 6days)
repquota -as # root 执行,可见 tom 带 + 标记
# 3) 继续写入直到超过硬限制(30MB)—— 写入被截断并报错
dd if=/dev/zero of=/home2/tom/f20m bs=1M count=20
# sdb1: write failed, user block limit reached.
# dd: 写入'/home2/tom/f20m' 出错: 超出磁盘限额
# dd: Disk quota exceeded
ls -lh /home2/tom/ # 文件只写到限额允许的大小就停止了
# 4) 验证 inode(文件数)配额
for i in $(seq 1 200); do touch /home2/tom/file$i; done
# touch: 无法创建 "/home2/tom/file101": 超出磁盘限额
ls /home2/tom | wc -l # 停在硬限制数量上
# 5) 清理并确认恢复
rm -f /home2/tom/f*
quota -s # 用量降回,警告消失,grace 计时清除
exit
宽限期到期的验证:把宽限期改成 1 分钟观察软限制"转硬"的效果:
setquota -t 60 60 /home2 # 宽限期 60 秒
su - tom -c 'dd if=/dev/zero of=/home2/tom/big bs=1M count=25' # 超软限制
sleep 70
su - tom -c 'dd if=/dev/zero of=/home2/tom/big2 bs=1M count=1'
# 宽限期已过:即使未到硬限制也无法写入,软限制变成了硬限制
setquota -t 604800 604800 /home2 # 恢复 7 天
配额的维护与自动化
quotacheck -avug # 定期校准配额数据库(建议在低峰期或单用户模式)
warnquota # 给超限用户自动发邮件告警(配置文件 /etc/warnquota.conf)
crontab -e
# 0 4 * * * /usr/sbin/warnquota # 每天 4 点检查并发告警邮件
# 0 3 * * 0 /usr/sbin/quotacheck -avug # 每周校准
# CentOS 6 服务方式(配额随开机自动启用由 fstab 选项驱动,无独立服务)
chkconfig --list | grep quota
# CentOS 7 也是由 systemd 的 quotaon.service 自动处理
systemctl status quotaon.service
6.3 xfs 的配额差异(CentOS 7 重点)
xfs 的配额机制与 ext4 完全不同,关键差异:
| 对比项 | ext2/3/4 | xfs |
|---|---|---|
| 挂载选项 | usrquota、grpquota |
uquota(或 usrquota)、gquota(或 grpquota)、pquota(项目配额) |
| 配额数据库 | 需要 quotacheck 生成 aquota.user/aquota.group 文件 |
内建在文件系统元数据中,不需要 quotacheck,也没有 aquota 文件 |
| 启用方式 | quotaon |
挂载时由挂载选项决定,挂载后不能用 remount 添加/移除配额(必须卸载重挂或重启) |
| 主要工具 | edquota/setquota/repquota/quota |
xfs_quota(也兼容 quota/repquota 查看) |
| 目录级限额 | 不支持 | 支持 project quota(按目录限额) |
| 组配额与项目配额 | 可同时使用 | gquota 与 pquota 互斥,不能同时启用 |
启用 xfs 配额
mkfs.xfs -f /dev/sdb1
mkdir -p /xfsdata
mount -o uquota,gquota /dev/sdb1 /xfsdata # 用户 + 组配额
mount -o uquota,pquota /dev/sdb1 /xfsdata # 用户 + 项目配额(gquota 与 pquota 互斥)
mount | grep xfsdata
写入 fstab:
/dev/sdb1 /xfsdata xfs defaults,uquota,gquota 0 0
UUID=xxxx /xfsdata xfs defaults,uquota,pquota 0 0
umount /xfsdata && mount /xfsdata # xfs 配额不能 remount 添加,必须重新挂载
对根分区启用 xfs 配额需要修改内核启动参数:在
/etc/default/grub的GRUB_CMDLINE_LINUX中加入rootflags=uquota,gquota,然后grub2-mkconfig -o /boot/grub2/grub.cfg并重启。
xfs_quota 命令用法
# -x 专家模式(可修改限额),-c 执行命令,不加 -c 则进入交互式
xfs_quota -x -c 'state' /xfsdata # 查看配额启用状态
xfs_quota -x -c 'report' /xfsdata # 报告配额使用情况
xfs_quota -x -c 'report -h' /xfsdata # 易读单位
xfs_quota -x -c 'report -u -h' /xfsdata # 只看用户
xfs_quota -x -c 'report -g -h' /xfsdata # 只看组
xfs_quota -x -c 'report -p -h' /xfsdata # 只看项目
xfs_quota -c 'quota -u tom' /xfsdata # 查看某用户配额
xfs_quota -c 'quota -h tom' /xfsdata # 易读单位
xfs_quota -x -c 'free -h' /xfsdata # 查看文件系统空间
# 设置用户配额:bsoft/bhard 空间软硬限制,isoft/ihard 文件数软硬限制
xfs_quota -x -c 'limit -u bsoft=200M bhard=300M tom' /xfsdata
xfs_quota -x -c 'limit -u isoft=800 ihard=1000 tom' /xfsdata
xfs_quota -x -c 'limit -u bsoft=200M bhard=300M isoft=800 ihard=1000 tom' /xfsdata
# 设置组配额
xfs_quota -x -c 'limit -g bsoft=1G bhard=2G devgrp' /xfsdata
# 设置宽限期
xfs_quota -x -c 'timer -u -bsoft 3days' /xfsdata
xfs_quota -x -c 'timer -u 7days' /xfsdata
xfs_quota -x -c 'timer -g -bsoft 1day' /xfsdata
# 开关配额记账与强制执行
xfs_quota -x -c 'disable -u' /xfsdata # 临时停止强制执行(仍记账)
xfs_quota -x -c 'enable -u' /xfsdata # 恢复强制执行
xfs_quota -x -c 'off -u' /xfsdata # 彻底关闭(关闭后只能卸载重挂才能再开)
xfs_quota -x -c 'remove -u' /xfsdata # 清除所有用户限额(须先 off)
# 交互式模式
xfs_quota -x /xfsdata
xfs_quota> state
xfs_quota> limit -u bsoft=100m bhard=200m tom
xfs_quota> report -h
xfs_quota> quit
xfs project quota:对目录限额的完整步骤
这是 xfs 独有的强大功能——限制某个目录树的总大小,与用户无关(适合限制 /var/log/nginx、每个项目组的共享目录、容器数据目录)。
# 1. 挂载时启用 pquota(注意与 gquota 互斥)
umount /xfsdata
mount -o uquota,pquota /dev/sdb1 /xfsdata
# fstab: /dev/sdb1 /xfsdata xfs defaults,uquota,pquota 0 0
xfs_quota -x -c 'state' /xfsdata # 确认 Project quota state 为 ON
# 2. 创建要限额的目录
mkdir -p /xfsdata/project_a /xfsdata/project_b
# 3. 定义项目 ID 与项目名的映射(/etc/projid)
cat >> /etc/projid <<'EOF'
proja:100
projb:101
EOF
# 4. 定义项目 ID 与目录的对应关系(/etc/projects)
cat >> /etc/projects <<'EOF'
100:/xfsdata/project_a
101:/xfsdata/project_b
EOF
# 5. 初始化项目(把目录树打上 project ID 标记)
xfs_quota -x -c 'project -s proja' /xfsdata
xfs_quota -x -c 'project -s projb' /xfsdata
xfs_quota -x -c 'project -s -p /xfsdata/project_a 100' /xfsdata # 不用配置文件的写法
# 6. 设置项目限额
xfs_quota -x -c 'limit -p bsoft=500M bhard=1G proja' /xfsdata
xfs_quota -x -c 'limit -p bsoft=2G bhard=3G isoft=10000 ihard=12000 projb' /xfsdata
# 7. 查看项目配额
xfs_quota -x -c 'report -p -h' /xfsdata
# Project quota on /xfsdata (/dev/sdb1)
# Blocks
# Project ID Used Soft Hard Warn/Grace
# proja 12K 500M 1G 00 [------]
# 8. 验证:往目录里写超量数据(任何用户写入都受限)
dd if=/dev/zero of=/xfsdata/project_a/big bs=1M count=1100
# dd: 写入'/xfsdata/project_a/big' 出错: 设备上没有空间
df -h /xfsdata/project_a
# 注意:启用 project quota 后,df 查看该目录会显示"配额大小"而不是整个文件系统大小
# 9. 清理项目标记
xfs_quota -x -c 'project -C proja' /xfsdata # 清除目录上的 project ID
ext4 也在较新内核上支持 project quota(需
tune2fs -O project,quota并挂载prjquota),但 xfs 的实现更成熟、使用更广泛。
七、磁盘加密(LUKS)
7.1 应用场景与原理
LUKS(Linux Unified Key Setup)是 Linux 标准的块设备加密方案,基于内核 dm-crypt 实现。加密发生在块设备层,对上层文件系统完全透明。
应用场景:
- 笔记本/移动硬盘/U 盘丢失后防止数据被读取;
- 服务器下架、磁盘报废、返修时防止残留数据泄露;
- 合规要求(等级保护、GDPR、PCI-DSS 等)要求静态数据加密;
- 多租户环境中隔离敏感数据分区(如存放密钥、证书、用户隐私的分区)。
工作原理:LUKS 在设备头部写入一个 LUKS 头(header),其中包含加密算法参数和最多 8 个(LUKS2 为 32 个)密钥槽(key slot)。真正加密数据的是随机生成的主密钥(master key),每个密钥槽用用户口令(或 keyfile)派生的密钥把主密钥加密后保存。这样设计的好处是:可以设置多个口令、可以更换口令而无需重新加密全盘数据。
安装工具:
yum install cryptsetup -y
yum install cryptsetup* -y # 连相关包一起装
cryptsetup --version
cat /proc/crypto | grep -i aes # 确认内核支持的加密算法
lsmod | grep dm_crypt # 确认 dm_crypt 模块已加载
modprobe dm_crypt # 手工加载
7.2 创建与使用加密分区(完整流程)
前提:加密的分区必须是新创建的、未使用过的分区(正在使用或有数据的分区不能直接加密,
luksFormat会摧毁其上的全部数据)。
# ===== 1. 创建加密分区 =====
cryptsetup luksFormat /dev/sda5
交互提示:
WARNING!
========
This will overwrite data on /dev/sda5 irrevocably.
Are you sure? (Type uppercase yes): YES
Enter passphrase for /dev/sda5:
Verify passphrase:
- 确认时必须输入大写的
YES(yes、y都无效,这是有意的防误操作设计); - 然后设置加密密码,切记不可忘记,忘记则数据永久无法恢复。
带参数的高级用法:
cryptsetup luksFormat --type luks2 /dev/sda5 # 使用 LUKS2 格式(CentOS 8+/新版)
cryptsetup luksFormat -c aes-xts-plain64 -s 512 -h sha512 /dev/sda5
# -c/--cipher 加密算法(默认 aes-xts-plain64)
# -s/--key-size 密钥长度(256/512)
# -h/--hash 哈希算法
# -i/--iter-time PBKDF2 迭代毫秒数(越大越安全但解锁越慢,默认 2000)
cryptsetup luksFormat /dev/sda5 /root/mykey.bin # 用 keyfile 而非口令
cryptsetup -y -v luksFormat /dev/sda5 # -y 二次确认口令,-v 详细
# ===== 2. 解密分区(打开),生成解密后的设备文件(soul 为自定义映射名)=====
cryptsetup luksOpen /dev/sda5 soul
cryptsetup open /dev/sda5 soul # 新语法,等效
# 输入密码后,生成映射设备 /dev/mapper/soul
ls -l /dev/mapper/
lsblk # 可以看到 sda5 下挂了一个 TYPE=crypt 的 soul
# ===== 3. 首次使用需格式化解密后的设备 =====
mkfs -t ext4 /dev/mapper/soul
mkfs.xfs /dev/mapper/soul # 也可以用 xfs
# 注意:格式化的对象是 /dev/mapper/soul,绝不是 /dev/sda5!
# ===== 4. 挂载加密分区 =====
mkdir -p /mnt/secure
mount /dev/mapper/soul /mnt
mount /dev/mapper/soul /mnt/secure
df -hT | grep soul
# 此后对 /mnt 的操作即是对加密分区的操作,读写自动加解密,完全透明
echo "secret data" > /mnt/secret.txt
# ===== 5. 卸载并关闭加密分区 =====
umount /dev/mapper/soul
umount /mnt
cryptsetup luksClose soul
cryptsetup close soul # 新语法,等效
ls /dev/mapper/ # soul 已消失
# 关闭后直接读裸设备只能看到随机数据,无法识别文件系统
blkid /dev/sda5 # TYPE="crypto_LUKS"
mount /dev/sda5 /mnt # 报错:unknown filesystem type 'crypto_LUKS'
7.3 密钥管理
LUKS 支持多个密钥槽(LUKS1 有 8 个,编号 0~7),可以给不同的人/用途配置不同口令。
# 查看 LUKS 头信息与密钥槽占用情况(重要)
cryptsetup luksDump /dev/sda5
输出关键内容:
LUKS header information for /dev/sda5
Version: 1
Cipher name: aes
Cipher mode: xts-plain64
Hash spec: sha256
Payload offset: 4096
MK bits: 256
Key Slot 0: ENABLED
Iterations: 130000
Salt: ...
Key Slot 1: DISABLED
Key Slot 2: DISABLED
...
# 添加一个新口令(需要提供任一已有的正确口令来验证)
cryptsetup luksAddKey /dev/sda5
cryptsetup luksAddKey /dev/sda5 /root/keyfile # 把 keyfile 添加为新的解锁凭据
cryptsetup luksAddKey --key-slot 2 /dev/sda5 # 指定写入第 2 个槽
# 删除某个口令
cryptsetup luksRemoveKey /dev/sda5 # 交互输入要删除的那个口令
cryptsetup luksKillSlot /dev/sda5 1 # 按槽号删除(需另一个有效口令)
# 警告:删除最后一个可用密钥槽 = 数据永久丢失
# 更换口令(等价于 add + remove)
cryptsetup luksChangeKey /dev/sda5
cryptsetup luksChangeKey /dev/sda5 --key-slot 0
# 测试某个口令是否正确(不做任何修改)
cryptsetup luksOpen --test-passphrase /dev/sda5 && echo "口令正确"
# 生成一个安全的 keyfile(用于自动解锁)
dd if=/dev/urandom of=/root/luks-keyfile bs=512 count=8
chmod 400 /root/luks-keyfile
cryptsetup luksAddKey /dev/sda5 /root/luks-keyfile
cryptsetup luksOpen -d /root/luks-keyfile /dev/sda5 soul # 用 keyfile 解锁,无需交互
cryptsetup luksOpen --key-file /root/luks-keyfile /dev/sda5 soul
7.4 头部备份的重要性
LUKS 头部(含所有密钥槽)只有几 MB,但它一旦损坏,即使口令正确也永远无法解密数据。 头部可能被误操作破坏(错误的 dd、误 mkfs、分区表操作失误、坏道正好落在头部)。因此:
# 备份 LUKS 头部(强烈建议在 luksFormat 之后立即执行)
cryptsetup luksHeaderBackup /dev/sda5 --header-backup-file /root/sda5-luks-header.img
# 把备份文件保存到【其他机器/加密的 U 盘/离线介质】,并注意:
# 该备份文件包含密钥槽,拿到它 + 口令就能解密数据 → 必须妥善保护!
chmod 400 /root/sda5-luks-header.img
# 恢复头部
cryptsetup luksHeaderRestore /dev/sda5 --header-backup-file /root/sda5-luks-header.img
# 警告:恢复会覆盖当前头部,之后只有备份时那些口令有效(备份后新增的口令会失效)
# 使用分离式头部(把头部单独存放,裸盘上完全没有加密痕迹)
cryptsetup luksFormat --header /root/detached-header.img /dev/sda5
cryptsetup luksOpen --header /root/detached-header.img /dev/sda5 soul
忘记密码的后果:LUKS 使用强加密与 PBKDF2 密钥派生,没有任何后门、没有找回途径、暴力破解在计算上不可行。忘记全部口令 = 数据永久性丢失。因此:
- 口令必须存放在企业密码管理系统中,并有第二人可取;
- 至少配置 2 个密钥槽(一个日常口令 + 一个应急恢复口令/keyfile);
- 立刻做头部备份;
- 加密不能替代备份,重要数据仍需独立备份(备份本身也应加密)。
7.5 /etc/crypttab 与开机自动解锁
/etc/crypttab 定义开机时需要解锁的加密设备,格式为四个字段:
映射名 底层设备(建议 UUID) 密钥文件(none 表示交互输入口令) 选项
示例:
# 开机时提示输入口令
soul UUID=1a2b3c4d-5e6f-7890-abcd-ef1234567890 none luks
# 使用 keyfile 自动解锁(无需人工输入,但 keyfile 必须妥善保护)
data UUID=aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee /root/luks-keyfile luks
# keyfile 放在 U 盘上(拔掉 U 盘就无法启动,实现"物理钥匙"效果)
data /dev/sdb1 /media/usbkey/key.bin luks,keyfile-timeout=30
# 常用选项:luks, discard(SSD TRIM), noauto, timeout=, tries=3, nofail
secure UUID=xxxx none luks,discard,nofail
配合 /etc/fstab 挂载解锁后的映射设备:
/dev/mapper/soul /mnt/secure ext4 defaults 0 0
/dev/mapper/data /data xfs defaults,nofail 0 0
操作与验证:
blkid /dev/sda5 # 取 UUID(注意取的是 crypto_LUKS 的 UUID)
cryptsetup luksUUID /dev/sda5 # 直接查看 LUKS 设备的 UUID
vim /etc/crypttab
vim /etc/fstab
systemctl daemon-reload # CentOS 7 让 systemd 重新生成加密单元
systemctl list-units | grep crypt # 查看 systemd-cryptsetup@ 单元
dracut -f # 若根分区加密,需重建 initramfs
reboot # 验证开机流程
# 手工触发(不重启验证)
systemctl start systemd-cryptsetup@soul.service
mount -a
CentOS 6 也使用
/etc/crypttab,但由rc.sysinit处理,字段含义相同。
安全提醒:
- keyfile 自动解锁虽然方便,但 keyfile 与磁盘在同一台机器上时,加密对"整机被盗"几乎失去意义(攻击者拿到机器就拿到了钥匙)。真正的安全做法是:口令交互输入、keyfile 放在可移动介质、或使用 TPM/网络密钥服务(如 Clevis + Tang 实现网络绑定自动解锁)。
- 加密会带来一定 CPU 开销(现代 CPU 有 AES-NI 指令集加速,开销通常 <5%)。检查是否支持:
grep -m1 aes /proc/cpuinfo。
7.6 加密 LVM 与已有数据的加密
# 在 LVM 逻辑卷上加密(常见组合)
lvcreate -L 10G -n lvsecure vgdata
cryptsetup luksFormat /dev/vgdata/lvsecure
cryptsetup luksOpen /dev/vgdata/lvsecure secure
mkfs.xfs /dev/mapper/secure
mount /dev/mapper/secure /secure
# 或者反过来:先加密整块盘,再在解锁后的设备上建 LVM(CentOS 安装器的默认方案)
cryptsetup luksFormat /dev/sdb
cryptsetup luksOpen /dev/sdb crypt_sdb
pvcreate /dev/mapper/crypt_sdb
vgcreate vgcrypt /dev/mapper/crypt_sdb
lvcreate -l 100%FREE -n lvdata vgcrypt
mkfs.xfs /dev/vgcrypt/lvdata
# 对已有数据的分区加密(原地加密,高风险,务必先完整备份)
cryptsetup-reencrypt --new --reduce-device-size 32M /dev/sdb1 # 需要 cryptsetup 1.6+
# 更安全的做法:备份数据 → luksFormat → 恢复数据
八、软件 RAID 管理(mdadm)
RAID(Redundant Array of Independent Disks,独立磁盘冗余阵列)把多块磁盘组合成一个逻辑块设备(如 /dev/md0),用于提升读写性能、提供磁盘级冗余,或两者兼顾。阵列设备与普通分区一样,仍需经过「格式化 → 挂载」才能使用。
8.1 RAID 级别原理与对比
各级别原理:
- RAID 0(条带 Striping):数据被切块轮流写到各盘,多盘并行 → 读写最快、容量 100% 利用,但没有任何冗余,任一盘损坏则全部数据丢失(故障率是单盘的 N 倍)。
- RAID 1(镜像 Mirroring):数据完整地同时写入两块盘 → 可靠性最高,读可并行加速,写要双写略慢,容量只有 50%。
- RAID 5(分布式奇偶校验):数据 + 校验信息分散在所有盘上,任一盘损坏可用其余盘的数据 + 校验重算恢复 → 容量利用率 (n-1)/n,可坏 1 块,写入需读改写 + 计算校验有开销。
- RAID 6(双重奇偶校验):使用两种独立校验(P + Q),容量 (n-2)/n,可坏 2 块,写开销大于 RAID 5,适合大容量盘(重建期长)。
- RAID 10(1+0):先两两做镜像(RAID 1),再把镜像组条带化(RAID 0) → 读写都快、重建只需复制镜像盘(快且压力小),容量 50%。
- RAID 01(0+1):先条带再镜像,可靠性低于 RAID 10,实际很少使用。
- JBOD / linear:简单串联多块盘,无性能提升、无冗余。
| 级别 | 最少磁盘数 | 容量利用率 | 冗余能力 | 读写特点 | 典型用途 |
|---|---|---|---|---|---|
| RAID 0 | 2 | 100% | 无,坏 1 块即全部数据丢失 | 条带化并行,读写都最快 | 临时数据、缓存、可重建的中间结果 |
| RAID 1 | 2 | 50% | 每个镜像组可坏 1 块 | 读较快(可多盘并读),写略慢(需双写) | 系统盘、/boot、小容量关键数据 |
| RAID 5 | 3 | (n-1)/n | 可坏 1 块 | 读快,写有校验计算开销 | 通用存储,容量与冗余的折中方案 |
| RAID 6 | 4 | (n-2)/n | 可坏 2 块 | 读快,写开销大于 RAID 5(双校验) | 大容量磁盘阵列、重建时间长的场景 |
| RAID 10 | 4(偶数) | 50% | 每个镜像组各可坏 1 块 | 读写都快,重建代价小 | 数据库等高 IOPS + 高可靠场景 |
- RAID 10 是「先镜像(RAID 1)再条带(RAID 0)」的组合,性能与可靠性兼顾,代价是只有一半可用容量。
- 容量利用率中的
n指阵列中的数据盘数量(热备盘不计入可用容量)。
容量计算举例(每块 1TB):
| 配置 | 可用容量 | 可容忍故障 |
|---|---|---|
| 4 盘 RAID 0 | 4 TB | 0 块 |
| 4 盘 RAID 1(2 组镜像) | 2 TB | 每组 1 块 |
| 4 盘 RAID 5 | 3 TB | 1 块 |
| 4 盘 RAID 6 | 2 TB | 2 块 |
| 4 盘 RAID 10 | 2 TB | 每个镜像组 1 块 |
| 3 盘 RAID 5 + 1 热备 | 2 TB | 1 块(+自动顶替) |
8.2 硬件 RAID、软件 RAID 与 fakeraid
| 类型 | 实现方式 | 特点 |
|---|---|---|
| 硬件 RAID | 独立 RAID 卡,带处理芯片与缓存(常配电池/电容) | 性能好、对系统透明;成本高,卡损坏后通常需同型号卡才能读取阵列 |
| 软件 RAID | 由内核 md 驱动实现,用 mdadm 管理 |
免费、灵活,不绑定硬件,换机器仍可组装阵列;占用少量 CPU |
| fakeraid | 主板 BIOS「RAID」,实际仍靠驱动完成计算 | 兼具两者缺点:跨平台兼容性差、依赖特定主板驱动,Linux 下一般不建议使用 |
何时使用 mdadm:没有硬件 RAID 卡、需要跨主板/跨机器迁移阵列、虚拟机或云主机的多块数据盘、以及学习与测试环境。若已有带缓存的硬件 RAID 卡,则优先使用硬件 RAID,不要再叠加软件 RAID。
软 RAID / 硬 RAID / LVM 的关系与选择:
| 需求 | 推荐方案 |
|---|---|
| 只要冗余,不需要灵活调整大小 | 硬 RAID 或 mdadm,直接格式化阵列 |
| 只要灵活扩容,冗余靠底层存储/云盘 | 只用 LVM(云主机数据盘的典型场景) |
| 既要冗余又要灵活扩容(最常见) | RAID(硬或软)+ LVM 叠加:磁盘 → 阵列 → PV/VG/LV → 文件系统 |
| 只要性能不要冗余 | RAID 0 或 LVM 条带 -i |
| 单机想按卷差异化冗余级别 | LVM 内建 RAID(--type raid1/5/6/10) |
三者并不互斥:LVM 不提供磁盘冗余(除内建 RAID 类型外),RAID 不提供灵活的空间再分配,通常叠加使用。
8.3 安装 mdadm
yum install mdadm -y
mdadm --version
8.4 创建 RAID 阵列
创建前先用 fdisk 在各磁盘上建立分区(参考第二章),并把分区类型 ID 改为 fd(Linux raid autodetect)。也可以直接使用整块裸盘(如 /dev/sdb)。
# 准备 4 块盘的分区,类型设为 fd
for d in b c d e; do
echo -e "n\np\n1\n\n\nt\nfd\nw\n" | fdisk /dev/sd$d
done
partprobe
lsblk
# 用 4 个分区创建 RAID 5:3 块数据盘 + 1 块热备盘
mdadm -C /dev/md0 -l 5 -n 3 -x 1 /dev/sd{b,c,d,e}1
# 长选项等效写法(可读性更好,推荐写在文档/脚本里)
mdadm --create /dev/md0 --level=5 --raid-devices=3 --spare-devices=1 \
/dev/sdb1 /dev/sdc1 /dev/sdd1 /dev/sde1
| 选项 | 全称 | 作用 |
|---|---|---|
-C |
--create |
创建新阵列,后接阵列设备名 /dev/md0 |
-l |
--level |
指定 RAID 级别,如 0、1、5、6、10、linear |
-n |
--raid-devices |
组成阵列的活动磁盘数量(此处为 3) |
-x |
--spare-devices |
热备盘数量(此处为 1),成员盘故障时自动顶替并重建 |
-c |
--chunk |
条带大小(KB),如 -c 64,影响性能 |
-a yes |
--auto=yes |
自动创建设备文件 |
-e |
--metadata |
元数据版本(1.2 默认、0.90 老格式) |
-N |
--name |
阵列名称 |
/dev/sd{b,c,d,e}1是 Shell 的花括号展开,等价于/dev/sdb1 /dev/sdc1 /dev/sdd1 /dev/sde1。- 创建后阵列会立即开始初始同步,此时已可格式化使用,但性能较低,建议等同步完成。
其它级别的创建示例:
mdadm -C /dev/md0 -l 0 -n 2 /dev/sdb1 /dev/sdc1 # RAID 0
mdadm -C /dev/md1 -l 1 -n 2 /dev/sdb1 /dev/sdc1 # RAID 1
mdadm -C /dev/md1 -l 1 -n 2 -x 1 /dev/sd{b,c,d}1 # RAID 1 + 热备
mdadm -C /dev/md5 -l 5 -n 4 -c 128 /dev/sd{b,c,d,e}1 # RAID 5,128K 条带
mdadm -C /dev/md6 -l 6 -n 4 /dev/sd{b,c,d,e}1 # RAID 6
mdadm -C /dev/md10 -l 10 -n 4 /dev/sd{b,c,d,e}1 # RAID 10
mdadm -C /dev/md0 -l 1 -n 2 /dev/sdb missing # 先用一块盘建降级阵列,稍后补盘
8.5 查看阵列状态
# 查看阵列详细信息(级别、容量、成员盘状态、同步进度)
mdadm -D /dev/md0
mdadm --detail /dev/md0
# 查看内核当前所有阵列及同步进度
cat /proc/mdstat
watch -n 1 cat /proc/mdstat # 持续观察重建/同步进度
# 查看单个成员盘上的 RAID 超级块信息(属于哪个阵列、角色是什么)
mdadm -E /dev/sdb1
mdadm --examine /dev/sdb1
mdadm --examine --scan # 扫描所有盘上的阵列信息
# 其它
mdadm -Q /dev/md0 # 快速查询
lsblk # 树形查看,TYPE 列显示 raid5
blkid /dev/md0
mdadm -D即--detail(看阵列),mdadm -E即--examine(看成员盘)。/proc/mdstat中[UUU]表示三块盘均正常,[UU_]表示有一块盘缺失或损坏。
/proc/mdstat 输出解读:
Personalities : [raid6] [raid5] [raid4]
md0 : active raid5 sde1[4](S) sdd1[3] sdc1[1] sdb1[0]
10475520 blocks super 1.2 level 5, 512k chunk, algorithm 2 [3/3] [UUU]
[====>................] recovery = 22.5% (1180160/5237760) finish=1.2min speed=52007K/sec
unused devices: <none>
(S)表示热备盘(Spare),(F)表示故障盘(Faulty);[3/3]表示 3 个活动盘中 3 个正常;[UUU]每个U是一个正常盘,_表示缺失;recovery/resync/reshape行显示重建、同步、重构的进度与预计剩余时间。
mdadm -D 关键字段:State(clean / clean, degraded / clean, degraded, recovering)、Active Devices、Working Devices、Failed Devices、Spare Devices、UUID、成员盘列表及其 State(active sync / spare / faulty)。
8.6 格式化、挂载与开机自动挂载
# 格式化阵列设备
mkfs.xfs /dev/md0
mkfs.ext4 /dev/md0 # CentOS 6
mkfs.xfs -f -d su=512k,sw=2 /dev/md0 # 与条带对齐(su=chunk 大小,sw=数据盘数)
# 挂载使用
mkdir -p /data
mount /dev/md0 /data
df -hT /data
# 获取阵列的 UUID
blkid /dev/md0
在 /etc/fstab 中建议使用 UUID 而非 /dev/md0(阵列编号在某些情况下可能变为 /dev/md127):
UUID=8f5c1c2a-6b3e-4c7a-9f21-0d4b7c9e1a55 /data xfs defaults 0 0
修改后执行
mount -a验证,参考第三章 3.7 节。
8.7 保存阵列配置
mdadm -Ds > /etc/mdadm.conf
mdadm --detail --scan >> /etc/mdadm.conf # 追加写法
mdadm --detail --scan --verbose >> /etc/mdadm.conf # 含成员盘列表
cat /etc/mdadm.conf
/etc/mdadm.conf 内容示例:
ARRAY /dev/md0 metadata=1.2 spares=1 name=localhost:0 UUID=3a4b5c6d:7e8f9012:3456789a:bcdef012
MAILADDR root@example.com
-Ds即--detail --scan,扫描当前所有阵列并输出可用于配置文件的行(含 UUID 与成员盘信息)。- 系统启动时
mdadm依据/etc/mdadm.conf自动组装(assemble)阵列。若不保存,阵列可能无法自动激活,或被组装成/dev/md127这类随机编号,导致/etc/fstab按设备名挂载失败。 - 每次增删成员盘或新建阵列后都应重新生成该文件;若根分区或
/boot位于阵列上,还需执行dracut -f把配置写入 initramfs。
配置故障邮件告警与监控:
echo 'MAILADDR ops@example.com' >> /etc/mdadm.conf
systemctl enable --now mdmonitor # CentOS 7 的监控服务
service mdmonitor start # CentOS 6
mdadm --monitor --scan --daemonise --mail=ops@example.com # 手工启动监控守护
mdadm --monitor --scan --oneshot # 单次检查(可放 crontab)
8.8 故障模拟与恢复(完整演练)
# 1. 将 /dev/sdb1 标记为故障(-f 即 --fail),模拟磁盘损坏
mdadm /dev/md0 -f /dev/sdb1
mdadm --manage /dev/md0 --fail /dev/sdb1 # 等效长写法
# 2. 观察重建过程:热备盘会自动接替并开始同步
cat /proc/mdstat
watch -n 1 cat /proc/mdstat
# md0 : active raid5 sde1[4] sdd1[3] sdc1[1] sdb1[0](F)
# [3/2] [U_U] recovery = 35.8% ...
# 同时确认此期间 /data 仍然可读写(这就是冗余的价值)
ls /data; echo test > /data/t.txt
# 3. 从阵列中移除故障盘(-r 即 --remove)
mdadm /dev/md0 -r /dev/sdb1
mdadm --manage /dev/md0 --remove /dev/sdb1
# 4. 换上新盘后加入阵列(-a 即 --add),作为新的热备或直接参与重建
mdadm /dev/md0 -a /dev/sdb1
mdadm --manage /dev/md0 --add /dev/sdb1
# 5. 确认阵列恢复正常
mdadm -D /dev/md0
cat /proc/mdstat # 应回到 [3/3] [UUU] 且有 (S) 热备
重建(recovery/resync)期间阵列仍可读写,但性能下降且冗余度降低,应尽量避免此时再做高负载操作。
其它常用管理操作:
mdadm /dev/md0 --replace /dev/sdb1 # 在线替换(先同步到新盘再移除,不经历降级)
mdadm --grow /dev/md0 --raid-devices=4 # 扩展阵列成员数(RAID 5 加盘扩容)
mdadm --grow /dev/md0 --size=max # 成员盘变大后扩展阵列可用容量
mdadm --grow /dev/md1 --level=5 # 在线变更 RAID 级别(reshape,风险高,先备份)
mdadm --grow /dev/md0 --bitmap=internal # 启用写意图位图,加速重建
echo 200000 > /proc/sys/dev/raid/speed_limit_min # 提高重建速度下限(KB/s)
echo 50000 > /proc/sys/dev/raid/speed_limit_max # 限制重建速度,减少对业务影响
echo check > /sys/block/md0/md/sync_action # 主动一致性巡检(建议每月做)
cat /sys/block/md0/md/mismatch_cnt # 查看不一致块数量
8.9 停止与彻底删除阵列
# 1. 先卸载文件系统
umount /data
# 2. 停止阵列(-S 即 --stop),释放 /dev/md0
mdadm -S /dev/md0
mdadm --stop /dev/md0
mdadm --stop --scan # 停止所有阵列
# 3. 清除成员盘上的 RAID 元数据(超级块)
mdadm --zero-superblock /dev/sdb1
mdadm --zero-superblock /dev/sdc1
mdadm --zero-superblock /dev/sd{b,c,d,e}1 # 批量
wipefs -a /dev/sdb1 # 更彻底地清除所有文件系统/RAID 签名
# 4. 重新组装已停止的阵列(不是删除,而是恢复使用)
mdadm -A /dev/md0 /dev/sd{b,c,d}1 # 手工组装
mdadm --assemble --scan # 按 /etc/mdadm.conf 自动组装所有
mdadm -A --run /dev/md0 /dev/sdb1 /dev/sdc1 # 强制以降级状态启动
- 不执行
--zero-superblock时,残留的超级块会让系统在下次启动时又把这些盘自动组装成阵列,或干扰该盘作为普通分区使用。 - 同时记得删除
/etc/fstab与/etc/mdadm.conf中的相关条目。
8.10 在阵列上叠加 LVM
md 设备可以像普通分区一样作为 LVM 的物理卷,从而同时获得「冗余 + 灵活扩容」:
pvcreate /dev/md0
vgcreate vgdata /dev/md0
lvcreate --name lvapp --size 100G vgdata
mkfs.xfs /dev/vgdata/lvapp
mount /dev/vgdata/lvapp /app
LVM 的完整用法参考第五章。常见分层顺序为:物理磁盘 → md 阵列 → PV/VG/LV → 文件系统。
8.11 RAID 使用注意事项
- RAID 不是备份。它只能防止磁盘硬件故障,无法防止误删除、误格式化、勒索软件、文件系统损坏或整机灾难——这些错误会同步写入所有成员盘。重要数据仍必须有独立备份。
- RAID 5 存在重建二次故障风险。大容量磁盘(数 TB 以上)重建耗时可达数小时甚至数天,期间阵列没有冗余,若第二块盘也故障则数据全失;同批次磁盘往往寿命相近,风险更高。因此大盘场景更推荐 RAID 6(可坏 2 块)或 RAID 10(重建只需复制镜像盘,速度快、压力小)。
- 尽量使用容量、型号、转速一致的磁盘;阵列可用容量以最小的成员盘为准。
- 配置热备盘(
-x)可以在故障发生时立即自动重建,缩短无冗余窗口。 - 定期检查
/proc/mdstat或配置监控告警,避免一块盘坏掉数月都无人发现。 - 创建 RAID 后务必执行
mdadm -Ds > /etc/mdadm.conf,否则重启后阵列可能无法自动组装。 - 定期做一致性巡检(
echo check > /sys/block/md0/md/sync_action),及早发现静默数据损坏。
九、磁盘性能与日常运维
9.1 IO 性能指标
| 指标 | 含义 | 关注场景 |
|---|---|---|
| IOPS | 每秒 I/O 操作次数 | 随机小 I/O 为主的业务(数据库、虚拟化、消息队列) |
| 吞吐量 Throughput | 每秒传输的字节数(MB/s) | 大文件顺序读写(备份、视频、日志归档、大数据) |
| 延迟 Latency | 单次 I/O 从发起到完成的时间(ms) | 对响应时间敏感的业务;await 是最重要的观察值 |
| 队列深度 Queue Depth | 同时在处理/等待的 I/O 数量 | 判断设备是否已成瓶颈 |
| 读写比例 | 读与写的比例 | 决定选用哪种 RAID 级别与缓存策略 |
经验参考值:
| 设备 | 随机 IOPS | 顺序吞吐 | 单次延迟 |
|---|---|---|---|
| 7200 转 SATA HDD | 75~100 | 100~160 MB/s | 8~15 ms |
| 15000 转 SAS HDD | 150~200 | 150~200 MB/s | 3~6 ms |
| SATA SSD | 5万~10万 | 400~550 MB/s | 0.05~0.2 ms |
| NVMe SSD | 20万~100万+ | 2000~7000 MB/s | 0.01~0.1 ms |
三者关系:吞吐量 ≈ IOPS × 单次 I/O 大小。同一块盘做 4K 随机与 1M 顺序,IOPS 和吞吐会呈现完全不同的数值,因此测试和评估必须说明 I/O 模型。
9.2 dd 测试顺序读写
# 顺序【写】测试:写 1GB
dd if=/dev/zero of=/data/testfile bs=1M count=1024
# 注意:这个结果偏乐观,因为数据可能还在页缓存里没落盘
# 加 oflag=direct 绕过页缓存,测真实磁盘写性能(推荐)
dd if=/dev/zero of=/data/testfile bs=1M count=1024 oflag=direct
# 加 conv=fdatasync:写完后强制刷盘再计时结束(也较真实)
dd if=/dev/zero of=/data/testfile bs=1M count=1024 conv=fdatasync
# oflag=dsync:每次写都同步(测最坏情况的写延迟,数值会非常低)
dd if=/dev/zero of=/data/testfile bs=1M count=1024 oflag=dsync
# 顺序【读】测试:先清空缓存,否则读的是内存
sync; echo 3 > /proc/sys/vm/drop_caches
dd if=/data/testfile of=/dev/null bs=1M count=1024
dd if=/data/testfile of=/dev/null bs=1M count=1024 iflag=direct # 绕过缓存
# 小块随机写(模拟数据库场景,观察 IOPS)
dd if=/dev/zero of=/data/testfile bs=4k count=100000 oflag=direct
# 清理
rm -f /data/testfile
dd 输出解读:1073741824 bytes (1.1 GB) copied, 8.2 s, 131 MB/s —— 最后的 MB/s 即吞吐量。
dd只能测顺序 I/O,且是单线程,不能反映真实并发场景,仅适合快速验证"这块盘大致是什么水平"。
9.3 hdparm 测试
hdparm -tT /dev/sda
# /dev/sda:
# Timing cached reads: 12000 MB in 2.00 seconds = 6000 MB/sec <- 内存/缓存读,不代表磁盘
# Timing buffered disk reads: 400 MB in 3.01 seconds = 132 MB/sec <- 磁盘顺序读,这个才有意义
hdparm -t --direct /dev/sda # 绕过缓存
hdparm -Tt /dev/sda; hdparm -Tt /dev/sda # 多测几次取平均
9.4 fio 简述(专业 IO 压测工具)
yum install fio -y
# 4K 随机读,测 IOPS(数据库场景)
fio -name=randread -filename=/data/fiotest -rw=randread -bs=4k -size=1G \
-numjobs=4 -iodepth=32 -direct=1 -runtime=60 -time_based -group_reporting
# 4K 随机写
fio -name=randwrite -filename=/data/fiotest -rw=randwrite -bs=4k -size=1G \
-numjobs=4 -iodepth=32 -direct=1 -runtime=60 -time_based -group_reporting
# 1M 顺序读,测吞吐
fio -name=seqread -filename=/data/fiotest -rw=read -bs=1M -size=2G \
-numjobs=1 -iodepth=16 -direct=1 -group_reporting
# 70% 读 30% 写的混合模型(更贴近真实业务)
fio -name=mix -filename=/data/fiotest -rw=randrw -rwmixread=70 -bs=4k -size=2G \
-numjobs=8 -iodepth=32 -direct=1 -runtime=120 -time_based -group_reporting
关键参数:-rw(read/write/randread/randwrite/randrw)、-bs(块大小)、-iodepth(队列深度)、-numjobs(并发线程)、-direct=1(绕过缓存,必加)、-runtime + -time_based(按时间跑)、-group_reporting(汇总输出)。
结果关注:IOPS、BW(带宽)、lat(延迟的 avg / 99th percentile)。测试务必在非生产磁盘上进行,fio 写测试会破坏指定文件。
9.5 iostat 各列含义与判读
yum install sysstat -y
iostat # 开机以来的平均值(第一次输出,参考意义有限)
iostat 2 # 每 2 秒刷新一次
iostat -x 2 10 # -x 扩展统计,每 2 秒一次共 10 次(最常用)
iostat -xd 2 # 只看磁盘(-d),不看 CPU
iostat -xdm 2 # -m 以 MB 为单位
iostat -xdk 2 # -k 以 KB 为单位
iostat -x -p sda 2 # -p 显示指定设备及其各个分区
iostat -xN 2 # -N 显示 LVM 设备名而非 dm-0
典型输出:
Device: rrqm/s wrqm/s r/s w/s rkB/s wkB/s avgrq-sz avgqu-sz await r_await w_await svctm %util
sda 0.00 12.50 25.00 75.00 1200.0 6000.0 72.00 8.50 85.00 10.00 110.00 2.00 98.50
各列含义:
| 列 | 含义与判读 |
|---|---|
rrqm/s / wrqm/s |
每秒被合并的读/写请求数。数值高说明内核成功合并了相邻 I/O,是好事 |
r/s / w/s |
每秒完成的读/写请求数,两者之和就是 IOPS |
rkB/s / wkB/s |
每秒读/写的数据量(吞吐量) |
avgrq-sz |
平均每个请求的扇区数(× 512 得字节)。值小说明是随机小 I/O,值大说明是顺序大 I/O |
avgqu-sz |
平均 I/O 队列长度。持续 > 1 说明有排队,> 10 说明严重拥塞 |
await |
平均每次 I/O 的等待 + 服务总时间(ms),最重要的指标。SSD 应 < 1ms,HDD 正常 < 20ms;远大于 svctm 说明队列在排队 |
r_await / w_await |
分别的读/写等待时间,可判断是读慢还是写慢 |
svctm |
平均服务时间(新版已废弃、不可信,不要依赖它) |
%util |
设备繁忙百分比(有 I/O 在处理的时间占比) |
判读要点:
%util接近 100% 说明设备"一直在忙"。但对 SSD/RAID/多队列设备而言,%util100% 不一定意味着饱和(它们能并行处理多个 I/O),此时应看await和avgqu-sz;await明显升高(HDD > 50ms、SSD > 5ms)且avgqu-sz> 几,说明磁盘已成瓶颈;%util高但r/s + w/s很低、await很高 → 典型的随机小 I/O 打满机械盘,考虑换 SSD 或加缓存;%util低但业务慢 → 瓶颈不在磁盘,往 CPU/内存/网络/锁 上找;- 结合
iostat -c看 CPU 的%iowait:%iowait高说明 CPU 在等 I/O。
9.6 iotop 找出高 IO 进程
yum install iotop -y
iotop # 交互式实时显示各进程的 I/O(需 root)
iotop -o # 只显示真正在产生 I/O 的进程(最常用)
iotop -oPa # -P 只看进程不看线程,-a 显示累计值
iotop -b -n 3 -o # 批处理模式输出 3 次(便于脚本/日志记录)
iotop -b -o -n 1 >> /var/log/iotop.log # 定时采样记录
iotop -u mysql # 只看指定用户
交互按键:o 切换只看活动进程,p 切换进程/线程,a 切换累计模式,←/→ 切换排序列,r 反转排序。
其它相关工具:
pidstat -d 2 # 按进程统计 I/O(sysstat 包)
vmstat 2 # bi/bo 列看块设备读写,wa 列看 I/O 等待
sar -d 2 5 # 历史 I/O 数据(需开启 sysstat 采集)
sar -d -f /var/log/sa/sa22 # 查看某天的历史数据(事后排查必备)
dstat -d --disk-util --top-io # 综合视图(需装 dstat)
cat /proc/<pid>/io # 查看单个进程的累计读写字节数
blktrace -d /dev/sda -o - | blkparse -i - # 内核级 I/O 跟踪(深度分析)
biolatency # bcc/eBPF 工具,I/O 延迟直方图
磁盘 I/O 调度器(
cfq/deadline/noop/mq-deadline)、脏页回写参数(vm.dirty_ratio)、预读(blockdev --setra)等系统级调优> 详见 04-03-Linux系统优化.md
9.7 磁盘空间日常巡检与告警
找出大文件与大目录
# 逐层缩小范围(最实用的手法)
du -h --max-depth=1 / | sort -rh | head -20
du -h --max-depth=1 /var | sort -rh | head -20
du -h --max-depth=1 /var/log | sort -rh | head -20
du -sh /* 2>/dev/null | sort -rh | head # 一层写法
du -x -h --max-depth=1 / # -x 不跨文件系统(避免统计到别的挂载点)
# 用 find 直接找大文件
find / -xdev -type f -size +500M -exec ls -lh {} \; 2>/dev/null
find / -xdev -type f -size +1G -printf '%s %p\n' 2>/dev/null | sort -rn | head -20
find /var -type f -size +100M | xargs ls -lhS
find / -xdev -type f -size +100M -mtime +90 # 又大又久没动的文件(清理候选)
find /data -type f -name '*.log' -size +1G
# 按大小排序显示某目录下的文件
ls -lhS /var/log | head -20
du -a /var | sort -rn | head -20 # 注意:不带 -h 才能正确排序
ncdu / # 交互式磁盘用量浏览器(yum install ncdu)
磁盘空间告警脚本思路
思路要点:
- 用
df -hP(-P保证一行一条,避免长设备名换行)取使用率; awk去掉%转成数字与阈值比较;- 排除 tmpfs、devtmpfs、iso9660 等无意义条目;
- 同时检查 inode 使用率(
df -iP),这是最容易漏掉的一项; - 触发时输出/发邮件/推送到监控系统,并附上 Top 大目录,便于直接定位;
- 加入 crontab 定时执行,并做告警抑制(同一分区短时间内不重复告警)。
#!/bin/bash
# /usr/local/bin/disk_alert.sh —— 磁盘与 inode 使用率告警
THRESHOLD=85
MAILTO="ops@example.com"
# 1) 检查空间使用率
df -hP -x tmpfs -x devtmpfs -x iso9660 | awk 'NR>1 {gsub("%","",$5); print $5, $6}' |
while read use mount; do
if [ "$use" -ge "$THRESHOLD" ]; then
echo "[WARN] 磁盘空间告警:$mount 已使用 ${use}%"
echo "--- Top 5 大目录 ---"
du -x -h --max-depth=1 "$mount" 2>/dev/null | sort -rh | head -6
fi
done
# 2) 检查 inode 使用率(同样重要)
df -iP -x tmpfs -x devtmpfs | awk 'NR>1 {gsub("%","",$5); print $5, $6}' |
while read iuse mount; do
if [ "$iuse" -ge "$THRESHOLD" ]; then
echo "[WARN] inode 告警:$mount inode 已使用 ${iuse}%"
fi
done
chmod +x /usr/local/bin/disk_alert.sh
crontab -e
# */10 * * * * /usr/local/bin/disk_alert.sh | mail -s "磁盘巡检 $(hostname)" ops@example.com
# 仅在有输出时才发邮件:用 ifne(moreutils)或在脚本内判断后自行发送
生产环境更推荐用 Zabbix / Prometheus node_exporter 的
node_filesystem_avail_bytes、node_filesystem_files_free做监控告警,脚本仅作应急与补充。
删除文件但空间未释放
现象:rm 掉了几十 GB 的日志,但 df -h 显示可用空间没有增加,而 du -sh 统计的用量已经变小(df 与 du 结果不一致)。
原因:文件的 inode 链接数已归零,但仍有进程持有该文件的文件描述符(fd),内核不会释放数据块,直到最后一个 fd 关闭。这是"正在被写入的日志文件被直接 rm"最典型的后果。
# 1. 确认症状:df 和 du 差距很大
df -h /var
du -sh /var
# 2. 找出已删除但仍被占用的文件(关键命令)
lsof +L1 # 列出所有 link count < 1 的打开文件(即已删除仍占用的)
lsof | grep deleted
lsof -n | grep -i deleted | sort -k7 -rn | head # 按大小排序
lsof +L1 /var # 只看某个文件系统
# 输出示例(NLINK 列为 0,SIZE 列显示占用的字节数)
# COMMAND PID USER FD TYPE DEVICE SIZE/OFF NLINK NODE NAME
# nginx 2345 root 5w REG 8,3 21474836480 0 13579 /var/log/nginx/access.log (deleted)
# 3. 处理方式(按优先级)
# 3a) 最优雅:让进程重新打开日志文件(不影响业务)
kill -USR1 <nginx_pid> # nginx 重新打开日志
systemctl reload nginx
kill -HUP <pid> # 多数守护进程用 HUP 重开日志
# 3b) 不重启进程也能立即释放空间:通过 /proc 清空该 fd
> /proc/2345/fd/5 # 把已删除文件的 fd 内容截断为 0,空间立即释放
truncate -s 0 /proc/2345/fd/5
# 3c) 兜底:重启持有该文件的服务(会短暂中断)
systemctl restart nginx
# 4. 验证空间已释放
df -h /var
lsof +L1
清理日志的正确做法
错误做法(会导致上面的"空间不释放"问题,或让服务丢失日志句柄):
rm -f /var/log/nginx/access.log # ❌ 不要直接删除正在被写入的日志
正确做法:
# 方式一:清空文件内容而保留 inode(进程的 fd 依然有效,立即释放空间)
> /var/log/nginx/access.log
: > /var/log/nginx/access.log
truncate -s 0 /var/log/nginx/access.log
cat /dev/null > /var/log/nginx/access.log
echo -n "" > /var/log/nginx/access.log
# 方式二:先归档保留再清空(推荐,保留了历史日志)
cp /var/log/nginx/access.log /backup/access-$(date +%F).log
> /var/log/nginx/access.log
gzip /backup/access-$(date +%F).log
# 方式三:正规做法——交给 logrotate 管理(生产标准)
cat > /etc/logrotate.d/nginx <<'EOF'
/var/log/nginx/*.log {
daily
rotate 14
compress
delaycompress
missingok
notifempty
create 0640 nginx adm
sharedscripts
postrotate
[ -f /var/run/nginx.pid ] && kill -USR1 $(cat /var/run/nginx.pid)
endscript
}
EOF
logrotate -d /etc/logrotate.d/nginx # -d 只演练不执行,验证配置
logrotate -f /etc/logrotate.d/nginx # -f 强制立即轮转一次
# 清理 systemd journal(CentOS 7)
journalctl --disk-usage
journalctl --vacuum-size=200M
journalctl --vacuum-time=7d
# 永久限制:/etc/systemd/journald.conf 中设置 SystemMaxUse=500M
日志轮转策略、集中式日志收集、journald 配置
> 详见 08-02-日志管理.md
稀疏文件(sparse file)
稀疏文件是"逻辑大小远大于实际占用空间"的文件,中间未写入的部分(空洞)不占磁盘块。虚拟机磁盘镜像、数据库预分配文件、truncate 创建的文件常是稀疏文件。
# 创建一个稀疏文件:逻辑 10G,实际占用 0
truncate -s 10G /data/sparse.img
dd if=/dev/zero of=/data/sparse.img bs=1M count=0 seek=10240
# 观察差异
ls -lh /data/sparse.img # 显示 10G(表观大小 apparent size)
du -h /data/sparse.img # 显示 0 或很小(实际占用)
du -h --apparent-size /data/sparse.img # 显示 10G
stat /data/sparse.img # Size 与 Blocks 差距巨大即为稀疏文件
filefrag -v /data/sparse.img # 查看 extent 分布与空洞
运维注意事项:
-
拷贝稀疏文件会"膨胀":普通
cp会把空洞填成真实的零,10G 稀疏文件复制后真占 10G。必须加参数:cp --sparse=always src dst rsync -S src dst # --sparse tar -Szcf backup.tar.gz /data # -S 处理稀疏文件 dd if=src of=dst conv=sparse -
df显示的用量比ls -l的总和小很多,可能就是稀疏文件造成的; -
稀疏文件被逐步写满会导致磁盘空间"意外"耗尽(如虚拟机磁盘镜像逐渐膨胀),需要预留余量或用
fallocate预分配。
回收与整理空间的其它手段
fstrim -av # 对所有支持的挂载点执行 TRIM(SSD/精简置备存储)
fstrim -v /data
systemctl enable --now fstrim.timer # CentOS 7 每周自动 TRIM
xfs_fsr /data # xfs 在线碎片整理
e4defrag /data # ext4 碎片整理
filefrag /data/bigfile # 查看文件碎片数
yum clean all # 清理 yum 缓存
rm -rf /var/cache/yum/*
package-cleanup --oldkernels --count=2 # 清理旧内核(/boot 满的常见原因)
find /tmp -type f -atime +10 -delete # 清理 /tmp 老文件
docker system prune -a # 清理 docker 无用镜像与卷(容器主机)
十、排错清单
10.1 报 No space left on device,但 df -h 显示还有空间
原因一:inode 耗尽(最常见)。分区里有海量小文件,数据块没用完但 inode 用光了。
df -h /data # Use% 只有 40%
df -i /data # IUse% 却是 100% ← 定位
# 找出小文件最多的目录
for d in /data/*; do echo "$(find $d -type f 2>/dev/null | wc -l) $d"; done | sort -rn | head
find /data -xdev -type f | awk -F/ '{print "/"$2"/"$3}' | sort | uniq -c | sort -rn | head
# 处理:删除无用小文件(如 session、缓存、邮件队列)
find /data/cache -type f -mtime +7 -delete
# 根治:备份数据后用更大的 inode 密度重新格式化(ext4 的 inode 数无法在线扩)
mkfs.ext4 -i 4096 /dev/sdb1 # 每 4KB 一个 inode
mkfs.ext4 -N 5000000 /dev/sdb1 # 直接指定 inode 总数
mkfs.ext4 -T small /dev/sdb1 # 使用 small 模板
# 或改用 xfs:inode 动态分配,不存在预先耗尽的问题(CentOS 7 默认)
原因二:已删除但被进程占用的文件(df 与 du 不一致)。
lsof +L1
> /proc/<pid>/fd/<n>
原因三:文件系统预留块被占满。ext 系列默认为 root 预留 5%,普通用户写入会提前报满。
tune2fs -l /dev/sdb1 | grep -i reserved
tune2fs -m 1 /dev/sdb1 # 把预留比例降到 1%,立即释放出可用空间
原因四:挂载点被覆盖。数据写到了挂载点下层的旧目录,实际占用的是根分区。
df -h /data # 看清 /data 到底落在哪个设备上
mount | grep data
mkdir /mnt/probe && mount --bind / /mnt/probe && du -sh /mnt/probe/data
原因五:磁盘配额限制(用户/组配额已达 hard limit)。
quota -u tom
repquota -a
xfs_quota -x -c 'report -h' /data
原因六:文件系统只读或损坏。
mount | grep ' / ' # 是否出现 (ro,...)
dmesg | tail -30 # 查找 I/O error、remounting read-only
10.2 磁盘写满导致服务无法启动
典型表现:数据库起不来、无法登录图形界面、sudo 报错、ssh 登录后立即断开、日志无法写入、yum 报错。
# 1. 应急腾空间(先让系统能动起来)
> /var/log/messages
journalctl --vacuum-size=100M
rm -f /var/log/*.gz /var/log/*.[0-9]
yum clean all; rm -rf /var/cache/yum/*
rm -rf /tmp/* /var/tmp/*
# 2. 若 / 已 100%,连 vim 都可能无法保存;用重定向或 sed -i 之外的手段
truncate -s 0 /var/log/httpd/access_log
# 3. 定位真凶
du -x -h --max-depth=1 / | sort -rh | head
lsof +L1
# 4. 检查 /boot 是否满(内核升级失败的常见原因)
df -h /boot
package-cleanup --oldkernels --count=2
rpm -qa kernel | sort -V
# 5. 长期措施:把 /var、/var/log、/home、/tmp 单独分区或做成 LV,
# 并配置磁盘告警,避免单个目录撑爆根分区
为什么根分区满了危害最大:/tmp、/var/run、/var/lib 都需要写;systemd、PAM、SELinux、数据库都会因无法写临时文件而失败。生产环境应当把易膨胀目录(/var/log、/home、/data)与根分区隔离。
10.3 /etc/fstab 配置错误导致系统无法启动
这是最经典也最容易致命的错误(在 fstab 里写错 UUID、设备名、文件系统类型,或写了不存在的设备)。
CentOS 7 修复流程:
# 1. 开机在 GRUB 菜单按 e 编辑启动项,在 linux16/linuxefi 行末追加:
# rd.break (进入 initramfs 阶段,根挂在 /sysroot 且为只读)
# 按 Ctrl+X 启动
# 2. 重新以读写方式挂载根
switch_root:/# mount -o remount,rw /sysroot
switch_root:/# chroot /sysroot
# 3. 修改 fstab(把错误行注释掉或改正)
sh-4.2# vi /etc/fstab
# 4. 校验语法与设备是否真实存在(关键一步,改完必须验证)
sh-4.2# findmnt --verify
sh-4.2# blkid
sh-4.2# mount -a # 无报错才算安全
# 5. 若启用了 SELinux,需重新打标签
sh-4.2# touch /.autorelabel
# 6. 退出重启
sh-4.2# exit
switch_root:/# reboot -f
另一种更快的方式(跳过 systemd 直接进 shell):
# GRUB 编辑行末加:init=/bin/bash (或 rw init=/sysroot/bin/sh)
mount -o remount,rw /
vi /etc/fstab
mount -a
exec /sbin/init # 或 reboot -f
CentOS 6 修复流程:
# 方式一:单用户模式(GRUB 菜单按 a 或 e,在 kernel 行末加 single 或 1)
# 进入后:
mount -o remount,rw /
vi /etc/fstab
mount -a
reboot
# 方式二:救援模式(用安装光盘启动 → Rescue installed system)
chroot /mnt/sysimage
vi /etc/fstab
exit; reboot
# 方式三:emergency 模式(连 / 都挂不上时,在 kernel 行加 emergency)
预防措施(务必养成习惯):
mount -a # 每次改完 fstab 立即执行,无报错才能重启
findmnt --verify # CentOS 7 语法与设备校验
cp /etc/fstab /etc/fstab.bak.$(date +%F) # 改前先备份
# 对非关键的数据盘加 nofail,缺盘时不阻塞开机
UUID=xxxx /data xfs defaults,nofail 0 0
rm -rf /etc/fstab的后果与恢复方法见文末"练习"部分的思考题。
10.4 UUID 变化导致挂载失败
触发场景:重新格式化了分区、克隆虚拟机后重建了文件系统、更换了磁盘、恢复了镜像、LVM 快照回滚。
# 1. 现象:开机卡在 Dependency failed for /data 或直接进 emergency shell
systemctl --failed
journalctl -xb | grep -i mount
systemctl status data.mount
# 2. 取当前真实的 UUID
blkid
blkid /dev/sdb1
lsblk -f
ls -l /dev/disk/by-uuid/
# 3. 对照并修正 fstab
vi /etc/fstab
mount -a
systemctl daemon-reload # CentOS 7 改完 fstab 必须重载,否则 .mount 单元仍用旧配置
systemctl restart local-fs.target
# 4. 另一条思路:把文件系统的 UUID 改回原值(保持 fstab 不变)
xfs_admin -U 12345678-1234-1234-1234-123456789abc /dev/sdb1 # xfs(需卸载)
xfs_admin -u /dev/sdb1 # 查看 xfs 的 UUID
tune2fs -U 12345678-1234-1234-1234-123456789abc /dev/sdb1 # ext2/3/4
tune2fs -U random /dev/sdb1 # 随机生成新 UUID
tune2fs -U $(uuidgen) /dev/sdb1
克隆虚拟机的特殊坑:整盘克隆会产生两块 UUID 完全相同的磁盘,挂载时随机命中其中一块,行为不可预测。必须为其中一块重新生成 UUID(xfs 用 xfs_admin -U generate,ext4 用 tune2fs -U random),LVM 则用 pvchange -u / vgchange -u 改 PV/VG 的 UUID。
根治建议:数据盘用 LABEL= 或 /dev/mapper/vg-lv(LVM 设备名稳定)挂载;不要用 /dev/sdb1 这类会随插槽顺序变化的名字。
e2label /dev/sdc1 /abc # ext 设置卷标
xfs_admin -L data /dev/sdb1 # xfs 设置卷标(需卸载)
# fstab 中写:LABEL=data /data xfs defaults 0 0
10.5 卸载时报 Device is busy
# 1. 查看是谁在占用
lsof /data # 打开该设备上文件的进程
lsof +D /data # 递归目录下所有打开的文件(较慢但更全)
fuser -mv /data # -m 挂载点,-v 详细;ACCESS 列 c=当前目录 f=打开文件 e=执行中 r=根目录
fuser -cu /data
# 2. 逐一处理(推荐,避免误杀)
cd / # 最常见原因:自己的 shell 还在 /data 里
kill <pid>
kill -9 <pid>
# 3. 批量杀(谨慎,会中断业务)
fuser -km /data # -k 杀掉所有占用进程,-m 按挂载点
fuser -k -TERM -m /data # 先温和地发 TERM
lsof -t /data | xargs kill -9
# 4. 特殊情况
umount -l /data # lazy umount:先从命名空间摘除,占用释放后自动完成
umount -f /data # 强制卸载(主要用于 NFS 无响应)
umount -R /mnt # 递归卸载其下所有子挂载点
umount -f -l /data # 网络文件系统失联时的组合拳
# 5. 容易忽略的占用来源
swapon -s # 该设备上有活动的 swap → swapoff /dev/sdb2
cat /proc/mounts | grep /data # 是否有 bind 挂载指向它 → 先卸载 bind
findmnt -R /data # 是否有子挂载点
mount | grep /data
exportfs -v # 是否被 NFS 导出 → exportfs -ua
docker ps # 容器是否挂载了该目录
dmsetup ls --tree # 是否被 device-mapper / LVM 引用
lsof | grep deleted | grep /data # 已删除但仍被占用的文件
losetup -a # 是否有 loop 设备关联该目录下的镜像文件
判断顺序建议:先 fuser -mv 看清全貌 → 若只是自己的 shell,cd / 即可 → 若是业务进程,走正常的 systemctl stop → 万不得已才 fuser -km 或 umount -l。
10.6 新建的分区在 lsblk 中看不到
# 1. 确认分区表里确实已经写入
fdisk -l /dev/sdb
parted /dev/sdb print
cat /proc/partitions # 内核当前认知的分区(这才是关键)
# 2. 通知内核重读分区表(三种手段,依次尝试)
partprobe /dev/sdb
partprobe # 不带参数则处理所有磁盘
partx -a /dev/sdb # 添加该磁盘的所有分区
partx -a --nr 3 /dev/sdb # 只添加第 3 个分区
partx -u /dev/sdb # 更新(分区大小改变后用)
partx -d /dev/sdb # 先全部删除再 -a 添加
kpartx -a /dev/sdb # multipath-tools 提供
kpartx -av /dev/loop0 # loop 设备用
udevadm settle # 等待 udev 处理完设备事件
udevadm trigger --subsystem-match=block
# 3. 报 "WARNING: Re-reading the partition table failed with error 16:
# Device or resource busy" 时
# → 说明该磁盘上有分区正在被挂载/被 swap/被 LVM 使用,内核拒绝整体重读
mount | grep sdb # 卸载该盘上已挂载的分区
swapoff /dev/sdb2
partx -a --nr 3 /dev/sdb # 仅让新分区单独生效,无需重读整张表
# 最终手段:reboot
# 4. 磁盘本身都看不到(新加的硬盘)
echo '- - -' > /sys/class/scsi_host/host0/scan
for h in /sys/class/scsi_host/host*/scan; do echo '- - -' > $h; done
rescan-scsi-bus.sh # sg3_utils 包
echo 1 > /sys/block/sdb/device/rescan # 扩容后刷新容量
lsblk; fdisk -l; dmesg | tail -20
扩容虚拟磁盘后容量没变:需要依次刷新三层——设备容量、分区大小、文件系统大小。
echo 1 > /sys/block/sdb/device/rescan # 1) 让内核看到新容量
lsblk /dev/sdb # 确认 SIZE 已变大
growpart /dev/sdb 1 # 2) 扩分区(cloud-utils-growpart)
# 或 parted /dev/sdb resizepart 1 100%
# 或 fdisk 删除再按原起始扇区重建(起始扇区必须一致!)
partprobe /dev/sdb
xfs_growfs /data # 3) 扩文件系统(xfs)
resize2fs /dev/sdb1 # ext4
10.7 resize2fs 报 "Nothing to do" 或 "文件系统已经是 xxx 个块"
原因:resize2fs 已经把文件系统扩到了它看到的设备边界,说明底层设备(分区 / LV)本身没有变大,扩容漏了前一步。
# 排查链条:设备容量 → 分区/LV 容量 → 文件系统容量,三者要逐层对齐
lsblk # 磁盘和分区各多大
pvs; vgs; lvs # LVM 三层容量
vgdisplay vg_data | grep Free # VG 里还有没有空闲 PE
df -h /data # 文件系统当前多大
# 常见漏掉的步骤:
# A) LVM 场景:忘了先 lvextend
vgextend vg_data /dev/sdc1 # VG 没空间时先加 PV
lvextend -L +10G /dev/vg_data/lv_data # 先扩 LV
lvextend -l +100%FREE /dev/vg_data/lv_data
resize2fs /dev/vg_data/lv_data # 再扩文件系统
# 一步完成(推荐):
lvextend -r -L +10G /dev/vg_data/lv_data
lvresize -r -L +10G /dev/vg_data/lv_data
# B) 普通分区场景:忘了先扩分区
parted /dev/sdb resizepart 1 100%
partprobe /dev/sdb
resize2fs /dev/sdb1
# C) 分区扩了但内核没重读 → 见 10.6
# D) 参数给错:resize2fs 的参数是【设备名】,xfs_growfs 的参数是【挂载点】
resize2fs /dev/vg_data/lv_data # ✅
resize2fs /data # ❌
xfs_growfs /data # ✅
xfs_growfs /dev/vg_data/lv_data # ❌(部分版本可接受,但不规范)
# E) 文件系统类型认错:对 xfs 用 resize2fs 会报 "Bad magic number in super-block"
df -T /data
blkid /dev/vg_data/lv_data
其它相关报错:
| 报错 | 原因与处理 |
|---|---|
Please run e2fsck -f first |
缩容或某些操作前必须先强制检查:e2fsck -f /dev/xxx |
On-line shrinking not supported |
ext4 不支持在线缩小,必须先 umount |
New size smaller than minimum (xxx) |
目标尺寸小于数据实际占用,用 resize2fs -P 查看最小可缩尺寸 |
Bad magic number in super-block |
文件系统类型不匹配,或超级块损坏(见 10.9) |
10.8 想缩小 xfs 文件系统
结论:xfs 不支持缩小,任何版本、任何工具都不行(xfs_growfs 只能增大,没有 shrink 功能)。这是 xfs 的设计取舍,也是 CentOS 7 默认用 xfs 后最常踩的坑。
唯一可行的路径是"备份 → 重建 → 恢复":
# 1. 完整备份数据(两种方式任选,建议异机保存)
xfsdump -l 0 -L full -M media0 -f /backup/data.dump /data
tar -czvf /backup/data.tar.gz -C /data .
rsync -avHAX /data/ /backup/data/ # 保留硬链接、ACL、扩展属性
# 2. 卸载并缩小底层 LV
umount /data
lvreduce -L 20G /dev/vg_data/lv_data # 会警告可能丢数据,确认 y
# 注意:这一步会直接截断 xfs,文件系统随即损坏 —— 所以必须先备份
# 3. 重新格式化
mkfs.xfs -f /dev/vg_data/lv_data
# 4. 挂载并恢复数据
mount /dev/vg_data/lv_data /data
xfsrestore -f /backup/data.dump /data
# 或 tar -xzvf /backup/data.tar.gz -C /data
# 或 rsync -avHAX /backup/data/ /data/
# 5. 校验数据完整性后再删除备份
diff -r /backup/data /data
替代思路:
- 如果目的是"把空间腾给别的 LV",可以考虑新建一个小 LV → 迁移数据 → 删除大 LV,效果相同且过程更可控;
- 需要频繁伸缩的场景,应当在规划阶段就选 ext4(支持离线缩小),或使用 LVM thin provisioning 按需分配;
- 生产环境的通用原则:LV 初始只分配所需容量,后续按需在线扩容,从根本上避免缩容需求。
| 文件系统 | 在线扩大 | 离线扩大 | 缩小 |
|---|---|---|---|
| ext2/3/4 | ✅ resize2fs |
✅ | ✅(必须卸载后操作) |
| xfs | ✅ xfs_growfs |
✅ | ❌ 完全不支持 |
| btrfs | ✅ btrfs filesystem resize |
✅ | ✅(支持在线缩小) |
10.9 LVM 缩容后文件系统损坏
根因:缩容的顺序颠倒了。lvreduce 直接把 LV 尾部的 PE 拿走,如果文件系统还认为自己是原来的大小,尾部的元数据和数据就被截断了。
正确顺序(必须严格遵守):先缩文件系统,再缩 LV。
umount /data
e2fsck -f /dev/vg_data/lv_data # 1. 强制检查(必做,否则 resize2fs 拒绝执行)
resize2fs /dev/vg_data/lv_data 10G # 2. 先把文件系统缩到 10G
lvreduce -L 10G /dev/vg_data/lv_data # 3. 再把 LV 缩到 10G(数值要一致或 LV 略大)
mount /dev/vg_data/lv_data /data # 4. 挂载验证
df -h /data
# 一步完成(更安全,工具自动按正确顺序处理):
lvreduce -r -L 10G /dev/vg_data/lv_data
lvresize -r -L 10G /dev/vg_data/lv_data
已经出事后的抢救步骤:
# 1. 立即停止一切写入,不要再挂载读写
# 2. 尝试把 LV 恢复到原来的大小(关键:只要 PE 还没被别的 LV 占用,就有救)
lvs -o +seg_pe_ranges
lvextend -L 30G /dev/vg_data/lv_data # 恢复到缩容前的尺寸
# 或用 LVM 元数据归档回滚(推荐)
ls -lt /etc/lvm/archive/ # 找到缩容前的那份归档
vgcfgrestore -l vg_data # 列出可用的备份版本
vgcfgrestore -f /etc/lvm/archive/vg_data_00012-xxxx.vg vg_data
vgchange -ay vg_data
# 3. 修复文件系统
e2fsck -f -y /dev/vg_data/lv_data
# 超级块损坏时用备份超级块
dumpe2fs /dev/vg_data/lv_data | grep -i superblock
e2fsck -b 32768 /dev/vg_data/lv_data
e2fsck -b 98304 -B 4096 /dev/vg_data/lv_data
# xfs
xfs_repair -n /dev/vg_data/lv_data # 先只检查不修改
xfs_repair /dev/vg_data/lv_data
xfs_repair -L /dev/vg_data/lv_data # 清空日志(最后手段,会丢数据)
# 4. 挂载后检查 lost+found,找回被修复程序摘出来的孤立文件
mount /dev/vg_data/lv_data /data
ls -l /data/lost+found
# 5. 若数据仍无法读取,使用底层恢复工具(先对整个 LV 做 dd 镜像再操作)
dd if=/dev/vg_data/lv_data of=/backup/lv.img bs=4M status=progress
testdisk /backup/lv.img
extundelete /backup/lv.img --restore-all
photorec
铁律:任何缩容操作前必须有完整备份;缩容不是常规运维动作,能扩容解决就不要缩容。
10.10 坏道检测(badblocks)
征兆:dmesg 出现 I/O error、Buffer I/O error on device、unrecoverable read error;读取某些文件时卡住或报输入/输出错误;文件系统频繁被自动 remount 成只读。
# 1. 先看内核日志确认是硬件层错误
dmesg | grep -iE 'error|fail|sense|medium'
dmesg -T | grep -i 'I/O error'
grep -iE 'error|fail' /var/log/messages | tail -30
# 2. badblocks 检测(务必对【未挂载】的设备操作)
badblocks -sv /dev/sdb # -s 显示进度,-v 详细;默认只读检测
badblocks -sv /dev/sdb1 > /root/badblocks.txt # 把坏块号输出到文件
badblocks -svn /dev/sdb1 # -n 非破坏性读写检测(慢,但更彻底,数据保留)
badblocks -svw /dev/sdb1 # -w 破坏性写测试(会清空数据!仅用于新盘/废盘)
badblocks -svw -t 0xff -t 0x00 -t 0xaa /dev/sdb1 # 指定多轮测试模式
badblocks -b 4096 -c 65536 -sv /dev/sdb1 # -b 块大小,-c 每次检测的块数(调大提速)
badblocks -sv -o /root/bb.list /dev/sdb1 # -o 输出坏块列表文件
# 3. 让文件系统避开这些坏块
# ext 系列:把坏块列表登记进文件系统,之后不再分配这些块
e2fsck -l /root/bb.list /dev/sdb1
e2fsck -cc /dev/sdb1 # 先做非破坏性读写检测再登记(-c 只读检测)
# 格式化时顺带检测(新盘上线前建议做一次)
mkfs.ext4 -c /dev/sdb1 # 只读检测
mkfs.ext4 -cc /dev/sdb1 # 读写检测(更慢更彻底)
mkfs.ext4 -l /root/bb.list /dev/sdb1 # 直接读入已有的坏块列表
# xfs 没有坏块登记机制,出现坏道应直接更换磁盘
# 4. 定位坏块影响了哪个文件(ext4)
debugfs -R "icheck 12345" /dev/sdb1 # 块号 → inode 号
debugfs -R "ncheck 6789" /dev/sdb1 # inode 号 → 文件名
dd if=/dev/sdb1 of=/dev/null bs=4096 skip=12345 count=1 # 验证该块是否真的读不了
重要判断:现代硬盘内部有备用扇区,会自动重映射坏道。一旦操作系统层面能看到坏块,说明备用扇区已经用尽或错误在快速增长,这块盘应当立即更换,不要试图靠 badblocks 屏蔽后继续使用生产数据。
10.11 smartctl 预警磁盘即将损坏
yum install smartmontools -y
# 1. 检查是否支持并已启用 SMART
smartctl -i /dev/sda # 基本信息,看 SMART support is: Enabled
smartctl -s on /dev/sda # 启用 SMART
# 2. 整体健康判定(最快的一眼判断)
smartctl -H /dev/sda
# SMART overall-health self-assessment test result: PASSED / FAILED!
# 出现 FAILED 表示磁盘已自评为即将失效 —— 立即更换
# 3. 查看全部属性(重点关注下表几项)
smartctl -A /dev/sda
smartctl -a /dev/sda # 全部信息(属性 + 日志 + 自检历史)
smartctl -x /dev/sda # 更详细
smartctl -l error /dev/sda # 错误日志
smartctl -l selftest /dev/sda # 自检历史记录
# 4. 特殊设备类型需指定 -d
smartctl -a -d sat /dev/sda # USB/SATA 桥接
smartctl -a -d nvme /dev/nvme0
smartctl -a -d megaraid,0 /dev/sda # LSI RAID 卡后面的物理盘
smartctl -a -d cciss,0 /dev/cciss/c0d0 # HP 阵列卡
smartctl --scan # 扫描所有可检测设备
# 5. 主动发起自检
smartctl -t short /dev/sda # 短测试(约 2 分钟)
smartctl -t long /dev/sda # 长测试(数小时,全盘扫描)
smartctl -t conveyance /dev/sda # 运输损伤检测
smartctl -X /dev/sda # 中止正在进行的自检
smartctl -l selftest /dev/sda # 查看自检结果
# 6. 开启守护进程做持续监控与邮件告警
systemctl enable --now smartd # CentOS 7
service smartd start; chkconfig smartd on # CentOS 6
# /etc/smartd.conf 示例:
# /dev/sda -a -o on -S on -s (S/../.././02|L/../../6/03) -m ops@example.com
关键 SMART 属性判读:
| ID | 属性名 | 含义与告警阈值 |
|---|---|---|
| 5 | Reallocated_Sector_Ct | 已重映射扇区数。> 0 就要警惕,持续增长必须换盘 |
| 197 | Current_Pending_Sector | 等待重映射的扇区数。> 0 说明有读不出的扇区,最危险的信号之一 |
| 198 | Offline_Uncorrectable | 无法校正的扇区数。> 0 表示已发生不可恢复的数据错误 |
| 187 | Reported_Uncorrect | 报告给主机的不可纠正错误数。> 0 是 Google/Backblaze 统计中最强的故障预测指标 |
| 188 | Command_Timeout | 命令超时次数,数值大说明链路或盘体有问题 |
| 1 | Raw_Read_Error_Rate | 读错误率(不同厂商含义不同,看趋势而非绝对值) |
| 7 | Seek_Error_Rate | 寻道错误率 |
| 194 | Temperature_Celsius | 温度。长期 > 50℃ 会显著缩短寿命,应改善散热 |
| 9 | Power_On_Hours | 通电小时数,判断磁盘服役年龄(> 3~5 年考虑轮换) |
| 12 | Power_Cycle_Count | 通电次数 |
| 241 | Total_LBAs_Written | 累计写入量。SSD 用它评估寿命消耗(对照厂商 TBW 标称值) |
| 177/231/233 | Wear_Leveling_Count / SSD_Life_Left | SSD 磨损均衡计数与剩余寿命百分比 |
VALUE 与 THRESH 的关系:VALUE 是归一化后的当前值,降到 THRESH 以下(WHEN_FAILED 列出现 FAILING_NOW)即为故障。RAW_VALUE 是原始计数,属性 5/197/198 要直接看 RAW_VALUE。
发现预警后的处置流程:
# 1. 立即做完整备份(最高优先级,不要先折腾诊断)
dd if=/dev/sda of=/backup/sda.img bs=4M conv=noerror,sync status=progress
ddrescue /dev/sda /backup/sda.img /backup/sda.log # 坏道多时用 ddrescue 更可靠
# 2. 若是 RAID 成员盘:走标准换盘流程
mdadm --detail /dev/md0
mdadm /dev/md0 --replace /dev/sdb1 # 有热备时先复制再剔除(更安全)
mdadm /dev/md0 -f /dev/sdb1 -r /dev/sdb1 # 标记故障并移除
# 换上新盘后
mdadm /dev/md0 -a /dev/sdf1
watch cat /proc/mdstat # 观察重建进度
# 3. 若是 LVM 的 PV:在线迁移数据到新盘,不中断业务
pvcreate /dev/sdf1
vgextend vg_data /dev/sdf1
pvmove /dev/sdb1 /dev/sdf1 # 在线搬迁(可用 pvmove 无参数继续被中断的迁移)
vgreduce vg_data /dev/sdb1
pvremove /dev/sdb1
# 4. 若是单盘系统盘:整盘克隆到新盘后替换
dd if=/dev/sda of=/dev/sdf bs=4M conv=noerror,sync status=progress
# 之后修复引导:grub2-install /dev/sdf
10.12 综合排错流程与常见错误对照表
通用排查顺序(从上层往下层,逐层排除):
业务报错
↓ df -h / df -i 空间与 inode 是否满
↓ mount | findmnt 是否挂载、是否被 remount 成 ro
↓ dmesg -T | tail 内核层是否有 I/O error(硬件问题的第一手证据)
↓ lsblk / blkid 设备与文件系统是否还在、UUID 是否变化
↓ pvs vgs lvs LVM 各层是否正常、VG 是否有缺失的 PV
↓ cat /proc/mdstat RAID 是否降级
↓ iostat -x 2 是否是性能瓶颈而非故障
↓ smartctl -H -A 磁盘硬件健康度
| 报错信息 | 常见原因 | 处理入口 |
|---|---|---|
No space left on device |
空间满 / inode 满 / 预留块 / 配额 | 10.1 |
Read-only file system |
文件系统检测到错误后自我保护 remount ro | dmesg、fsck、10.9 |
Device or resource busy |
有进程/swap/bind/LVM 占用 | 10.5、10.6 |
mount: unknown filesystem type |
类型写错、缺内核模块(ntfs、exfat) | blkid、装 ntfs-3g |
mount: special device ... does not exist |
设备名/UUID 错误或磁盘未识别 | 10.4、10.6 |
wrong fs type, bad option, bad superblock |
类型不符或超级块损坏 | blkid、e2fsck -b |
Bad magic number in super-block |
用错工具(对 xfs 用 e2fsck)或超级块损坏 | 10.7、10.9 |
Structure needs cleaning |
xfs 元数据损坏 | xfs_repair |
Input/output error |
硬件坏道或链路故障 | 10.10、10.11 |
Nothing to do!(resize2fs) |
底层设备未先扩容 | 10.7 |
Volume group "xxx" not found |
PV 丢失 / VG 未激活 | vgscan、vgchange -ay、vgcfgrestore |
Couldn't find device with uuid xxx(LVM) |
PV 磁盘掉线 | vgreduce --removemissing --force |
Insufficient free extents |
VG 空间不足 | vgextend 加 PV |
error 16: Device or resource busy(分区表) |
该盘有分区在使用中 | 10.6 用 partx --nr |
Dependency failed for /data(开机) |
fstab 错误 | 10.3、10.4 |
Timed out waiting for device |
fstab 里的设备不存在 | 加 nofail,10.3 |
练习
练习一:思考题(源自 01 文档)
1. 如果执行了 rm -rf /etc/fstab,会有什么后果?如何恢复?
后果分析:
- 当前运行的系统不会立刻崩溃。因为所有文件系统在开机时已经挂载完成,挂载信息记录在内核中(
/proc/mounts、/etc/mtab),运行中的业务不受影响。 - 但会立即出现以下问题:
mount -a、mount /data(省略设备名的简写形式)失效;df、mount命令仍可用,但swapon -a无法工作;- CentOS 7 上 systemd 无法生成
*.mount单元,systemctl daemon-reload后相关单元消失。
- 一旦重启,后果严重:
- 除了内核通过
root=参数挂载的根文件系统外,所有其它分区都不会被挂载(/boot、/home、/var、/data等); - swap 不会启用,内存吃紧;
- 根文件系统可能以只读方式留存(因为 fstab 中缺少
remount rw的记录,视发行版而定); - 依赖这些挂载点的服务(数据库、Web、NFS 导出)全部启动失败;
- 严重情况下系统会直接进入
emergency mode要求输入 root 密码。
- 除了内核通过
恢复方法(按优先级):
# 方法一(最佳):从备份恢复 —— 这就是为什么改 fstab 前必须先备份
cp /etc/fstab.bak /etc/fstab
# 方法二:趁系统还没重启,用当前的挂载信息重建(强烈推荐立刻做)
cat /proc/mounts # 当前所有挂载关系都在这里
cat /etc/mtab
mount # 人眼可读的形式
lsblk -f # 设备、类型、UUID、挂载点一览
blkid # 取各分区的 UUID、LABEL、TYPE
swapon -s # 找回 swap 分区
findmnt -s # 从 fstab 读(此时已空),findmnt 不带 -s 则从内核读
# 据此手写 fstab(示例)
cat > /etc/fstab <<'EOF'
UUID=8f3a1b2c-... / xfs defaults 0 0
UUID=1a2b3c4d-... /boot xfs defaults 0 0
UUID=5e6f7a8b-... swap swap defaults 0 0
/dev/mapper/vg_data-lv_data /data xfs defaults 0 0
EOF
# 方法三:从 RPM 包中提取(fstab 属于 setup 包,但包里是模板不含实际配置)
rpm -qf /etc/fstab # setup-x.x.x
cd /tmp && rpm2cpio /path/to/setup-*.rpm | cpio -idmv ./etc/fstab
# 方法四:已经重启且进不去系统 → 用 rd.break 或救援模式(见 10.3)后手写 fstab
# 无论用哪种方法,最后必须验证
findmnt --verify
mount -a # 无任何报错才可以重启
经验教训:/etc/fstab、/etc/passwd、/etc/shadow、/boot/grub2/grub.cfg 属于"改一个字符就可能开不了机"的文件,修改前一律先 cp xxx xxx.bak.$(date +%F)。
2. 如果 /etc/fstab 中写错了 UUID 或设备名,会发生什么?
| 场景 | CentOS 6 表现 | CentOS 7 表现 |
|---|---|---|
| 写错非关键数据盘的 UUID/设备名 | 开机时 mount -a 失败,屏幕给出错误提示,多数情况下仍能继续启动到登录界面,仅该目录未挂载 |
systemd 认为 local-fs.target 依赖失败,默认会中断启动并进入 emergency mode,要求输入 root 密码;除非该行加了 nofail |
| 写错根分区的 UUID | 内核找不到根文件系统,Kernel panic - not syncing: VFS: Unable to mount root fs |
同样 panic,或停在 dracut 的 initramfs> 提示符 |
| 写错文件系统类型(如 xfs 写成 ext4) | mount: wrong fs type, bad option, bad superblock |
同样报错,进入 emergency |
| 写错挂载选项(拼写错误) | mount: unknown option |
同上 |
| 设备当前不存在(比如已拔掉的 U 盘) | 报错但可继续 | 等待 90 秒后 Timed out waiting for device,进入 emergency |
| 第 6 列 fsck 顺序写错(非根分区写成 1) | 检查顺序混乱,可能导致检查失败 | 同上 |
危险的隐性错误:如果把某个数据盘的 UUID 误写成另一个已在使用的分区的 UUID,可能出现同一个设备被挂载到两个位置,或数据写入了错误的分区,造成数据混乱甚至覆盖,这比开不了机更危险。
恢复与预防:
# 恢复:进入救援环境(rd.break / single / 光盘救援)→ 修正 fstab → mount -a 验证(见 10.3、10.4)
# 预防(三条铁律)
mount -a # 1. 改完立刻验证,报错就说明有问题
findmnt --verify # 2. CentOS 7 专用的语法与设备校验
UUID=xxx /data xfs defaults,nofail 0 0 # 3. 非关键数据盘加 nofail,缺盘不阻塞开机
# 追加 x-systemd.device-timeout=10 可缩短等待时间
UUID=xxx /data xfs defaults,nofail,x-systemd.device-timeout=10 0 0
练习二:分区规划实操(源自 07 文档 练习 PPP1)
题目:在虚拟机中新增一块硬盘,按下表完成分区、格式化与挂载,并配置为开机自动挂载。
| 序号 | 分区用途 | 大小 | 文件系统类型 | 分区类型 ID | 挂载点 |
|---|---|---|---|---|---|
| 1 | Linux 数据分区 | 2 GB | ext4 | 83(Linux) | /mnt/data1 |
| 2 | Linux LVM 分区 | 4 GB | 交由 LVM 管理 | 8e(Linux LVM) | 由 LV 决定 |
| 3 | Linux swap 分区 | 1 GB | swap | 82(Linux swap) | swap |
| 4 | Windows 数据分区 | 2 GB | ntfs | 7(HPFS/NTFS) | /mnt/win |
| 5 | FAT32 共享分区 | 1 GB | vfat | b(W95 FAT32) | /mnt/fat |
原题中的编号 20 / 19 / 4 / 7 是
fdisk交互中l列表里的行号或不同版本的编号,实际输入的是上表的分区类型 ID(十六进制代码)。以t命令输入83、8e、82、7、b即可。
参考答案:
# 步骤 0:确认新硬盘已被识别
lsblk
fdisk -l /dev/sdb
# 步骤 1:分区(MBR 方案:3 个主分区 + 1 个扩展分区内放 2 个逻辑分区)
fdisk /dev/sdb
# n → p → 1 → 回车 → +2G 创建 sdb1(2G,数据)
# n → p → 2 → 回车 → +4G 创建 sdb2(4G,LVM)
# n → p → 3 → 回车 → +1G 创建 sdb3(1G,swap)
# n → e → 4 → 回车 → 回车 剩余空间全部给扩展分区
# n → 回车 → +2G 创建 sdb5(2G,NTFS)
# n → 回车 → +1G 创建 sdb6(1G,FAT32)
# t → 2 → 8e 把 sdb2 标记为 Linux LVM
# t → 3 → 82 把 sdb3 标记为 Linux swap
# t → 5 → 7 把 sdb5 标记为 HPFS/NTFS
# t → 6 → b 把 sdb6 标记为 W95 FAT32
# (sdb1 默认就是 83 Linux,无需修改)
# p 打印确认
# w 写入并退出
partprobe /dev/sdb # 刷新分区表
lsblk /dev/sdb
fdisk -l /dev/sdb # 核对 Id 与 System 两列
# 步骤 2:格式化
mkfs.ext4 -L data1 /dev/sdb1
mkswap -L myswap /dev/sdb3
yum install epel-release -y && yum install ntfs-3g -y # NTFS 支持需 EPEL 源
mkfs.ntfs -f -L windata /dev/sdb5 # -f 快速格式化
mkfs.vfat -F 32 -n SHARE /dev/sdb6 # -F 32 指定 FAT32,-n 卷标(必须大写)
# 步骤 3:LVM 部分(sdb2)
pvcreate /dev/sdb2
vgcreate vg_test /dev/sdb2
lvcreate -L 3G -n lv_test vg_test
mkfs.xfs /dev/vg_test/lv_test
mkdir -p /mnt/lvm
mount /dev/vg_test/lv_test /mnt/lvm
# 步骤 4:创建挂载点并挂载
mkdir -p /mnt/data1 /mnt/win /mnt/fat
mount /dev/sdb1 /mnt/data1
mount -t ntfs-3g /dev/sdb5 /mnt/win
mount -t vfat -o iocharset=utf8,umask=000 /dev/sdb6 /mnt/fat
swapon /dev/sdb3
free -m; swapon -s; df -hT
# 步骤 5:配置开机自动挂载
blkid # 取各分区 UUID
vi /etc/fstab
UUID=<sdb1的UUID> /mnt/data1 ext4 defaults 0 2
UUID=<sdb3的UUID> swap swap defaults 0 0
UUID=<sdb5的UUID> /mnt/win ntfs-3g defaults,nofail 0 0
UUID=<sdb6的UUID> /mnt/fat vfat defaults,iocharset=utf8,umask=000,nofail 0 0
/dev/vg_test/lv_test /mnt/lvm xfs defaults 0 0
# 步骤 6:验证(必做)
umount /mnt/data1 /mnt/win /mnt/fat /mnt/lvm
swapoff /dev/sdb3
mount -a && swapon -a
df -hT; free -m
findmnt --verify # CentOS 7
reboot # 最终验证
练习三:卸载繁忙设备实操(源自 07 文档 练习 PPP2)
题目:按以下步骤制造并解决"设备繁忙无法卸载"的场景。
- 新建一个分区并格式化、挂载到
/mnt/test; - 打开一个新终端,
cd进入/mnt/test,并在其中创建文件、用vim打开一个文件不退出; - 回到第一个终端,尝试
umount /mnt/test,观察报错; - 使用
lsof和fuser找出是哪些进程在占用; - 分别用"温和方式"和"强制方式"解决,最终成功卸载。
参考答案:
# ===== 步骤 1:准备环境 =====
fdisk /dev/sdb # n → 回车 → +1G → w
partprobe /dev/sdb
mkfs.xfs /dev/sdb7 # 或 mkfs.ext4
mkdir -p /mnt/test
mount /dev/sdb7 /mnt/test
df -hT | grep test
# ===== 步骤 2:制造占用(在【第二个终端】执行)=====
cd /mnt/test
touch a.txt b.txt
echo "hello" > a.txt
vim a.txt # 保持打开状态不退出
# 再开第三个终端执行 tail -f /mnt/test/a.txt 可制造更多占用
# ===== 步骤 3:尝试卸载(回到第一个终端)=====
umount /mnt/test
# umount: /mnt/test: target is busy.
# (In some cases useful info about processes that use
# the device is found by lsof(8) or fuser(1))
# CentOS 6 的提示为:
# umount: /mnt/test: device is busy.
# ===== 步骤 4:定位占用者 =====
lsof /mnt/test
# COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
# bash 3021 root cwd DIR 8,23 4096 128 /mnt/test <- cwd 停在该目录
# vim 3105 root cwd DIR 8,23 4096 128 /mnt/test
# vim 3105 root 4u REG 8,23 12288 131 /mnt/test/.a.txt.swp
lsof +D /mnt/test # 递归查看目录下所有被打开的文件
fuser -mv /mnt/test
# USER PID ACCESS COMMAND
# /mnt/test: root 3021 ..c.. bash
# root 3105 ..c.. vim
# ACCESS 含义:c=进程的当前工作目录在此 f=有打开的文件
# e=正在执行该设备上的程序 r=该设备是进程的根目录 m=映射了库文件
fuser -mu /mnt/test # 显示进程所属用户
fuser -cu /mnt/test
# ===== 步骤 5a:温和方式(推荐,不丢数据)=====
# 在第二个终端:先在 vim 里 :wq 保存退出,再 cd 离开该目录
# :wq
# cd /
# 回到第一个终端:
umount /mnt/test # 成功
df -hT | grep test # 已无输出
# ===== 步骤 5b:强制方式(会中断进程,有丢数据风险)=====
mount /dev/sdb7 /mnt/test # 重新挂载以便再演练一次
# (在第二个终端重新 cd 进去并 vim 打开文件)
kill 3105 # 逐个温和终止(先 TERM,给进程保存的机会)
fuser -k -TERM -m /mnt/test # 批量发 TERM
umount /mnt/test
# 若仍不行,升级手段:
fuser -km /mnt/test # 发 KILL 杀掉全部占用进程
lsof -t /mnt/test | xargs -r kill -9
umount /mnt/test
# 终极手段:延迟卸载(立即从目录树摘除,占用释放后自动完成真正的卸载)
umount -l /mnt/test
mount | grep test # 已看不到
lsof /mnt/test # 进程仍持有旧的 inode,但新访问已不通
# 网络文件系统失联时:
umount -f /mnt/test
umount -f -l /mnt/test
# ===== 步骤 6:清理 =====
umount /mnt/test 2>/dev/null
rmdir /mnt/test
结论与要点:
- 最常见的占用原因就是自己的 shell 还停在该目录里(
cwd),cd /即可解决,不要一上来就杀进程; vim会在目录里留下.xxx.swp交换文件,强杀后需要清理,且可能丢失未保存内容;- 生产环境优先走"停业务服务 → 卸载"的正规流程,
fuser -km属于应急手段; umount -l是最安全的兜底选择:不杀进程、不阻塞脚本,但要注意卸载是延迟生效的,紧接着的mount同一设备可能出现意外行为。
练习四:LVM 综合实操(补充)
题目:新增两块 10GB 硬盘,完成以下任务:
- 创建 VG
vg_app(PE 大小 16MB),包含这两块盘; - 创建 5GB 的 LV
lv_web,格式化为 xfs,挂载到/web; - 写入测试数据后,在线扩容到 12GB;
- 创建 LV
lv_db(3GB,ext4)挂载到/db,然后离线缩容到 2GB; - 为
lv_web创建 1GB 快照并验证快照内容; - 配置开机自动挂载。
参考答案:
# ===== 1. 创建 PV 与 VG =====
lsblk
pvcreate /dev/sdc /dev/sdd # 整盘做 PV(也可先分区并标记 8e)
pvs; pvdisplay
vgcreate -s 16M vg_app /dev/sdc /dev/sdd # -s 指定 PE 大小为 16MB
vgs; vgdisplay vg_app # 确认 PE Size = 16.00 MiB,总容量约 20G
# ===== 2. 创建 lv_web 并挂载 =====
lvcreate -L 5G -n lv_web vg_app
lvs; lvdisplay /dev/vg_app/lv_web
mkfs.xfs /dev/vg_app/lv_web
mkdir -p /web
mount /dev/vg_app/lv_web /web
df -hT /web
# 写入测试数据
dd if=/dev/zero of=/web/testfile bs=1M count=500
echo "web data v1" > /web/readme.txt
# ===== 3. 在线扩容到 12GB(业务不中断)=====
vgs vg_app # 确认 VFree 足够
lvextend -L 12G /dev/vg_app/lv_web # 扩 LV(也可写 -L +7G)
lvs
xfs_growfs /web # 扩 xfs 文件系统(参数是【挂载点】)
df -hT /web # 确认已变成 12G,且 /web 全程未卸载
cat /web/readme.txt # 数据完好
# 一步完成的写法:
# lvextend -r -L 12G /dev/vg_app/lv_web
# lvresize -r -L 12G /dev/vg_app/lv_web
# ===== 4. 创建 lv_db 并缩容(必须离线,且顺序不能错)=====
lvcreate -L 3G -n lv_db vg_app
mkfs.ext4 /dev/vg_app/lv_db
mkdir -p /db && mount /dev/vg_app/lv_db /db
echo "db data" > /db/data.txt
df -hT /db
umount /db # ① 卸载
e2fsck -f /dev/vg_app/lv_db # ② 强制检查(必做)
resize2fs /dev/vg_app/lv_db 2G # ③ 先缩【文件系统】
lvreduce -L 2G /dev/vg_app/lv_db # ④ 再缩【LV】,输入 y 确认
mount /dev/vg_app/lv_db /db # ⑤ 挂载验证
df -hT /db; cat /db/data.txt # 确认 2G 且数据完好
# 一步完成(更安全):lvreduce -r -L 2G /dev/vg_app/lv_db
# ===== 5. 创建快照 =====
lvcreate -L 1G -s -n lv_web_snap /dev/vg_app/lv_web
lvs # Origin 列显示 lv_web,Data% 显示快照已用比例
mkdir -p /snap
mount -o nouuid,ro /dev/vg_app/lv_web_snap /snap # xfs 快照必须加 nouuid
ls -l /snap; cat /snap/readme.txt # 内容与创建快照那一刻一致
# 验证快照的独立性:修改源卷,快照内容不变
echo "web data v2" > /web/readme.txt
cat /web/readme.txt # v2
cat /snap/readme.txt # 仍然是 v1 ← 快照生效
# 用快照做一致性备份
tar -czvf /backup/web-$(date +%F).tar.gz -C /snap .
# 从快照回滚(可选,需卸载源卷)
umount /snap /web
lvconvert --merge /dev/vg_app/lv_web_snap
mount /dev/vg_app/lv_web /web
cat /web/readme.txt # 回到 v1,快照自动删除
# 清理快照(不回滚时)
umount /snap
lvremove /dev/vg_app/lv_web_snap
# ===== 6. 配置开机自动挂载 =====
blkid /dev/vg_app/lv_web /dev/vg_app/lv_db
vi /etc/fstab
# /dev/mapper/vg_app-lv_web /web xfs defaults 0 0
# /dev/mapper/vg_app-lv_db /db ext4 defaults 0 2
# (LVM 设备名稳定,可直接使用;也可用 UUID)
umount /web /db && mount -a && df -hT
findmnt --verify
关键要点:
| 操作 | 顺序 | 是否需要卸载 |
|---|---|---|
| 扩容 | 先 lvextend,再 xfs_growfs/resize2fs |
不需要,可在线 |
| 缩容 | 先 resize2fs,再 lvreduce |
必须卸载,且 xfs 不支持 |
练习五:GPT 分区与大容量磁盘(补充)
题目:为一块 4TB 硬盘做 GPT 分区,划分为 1TB 和 3TB 两个分区,并验证 4K 对齐。
参考答案:
# 为什么必须用 GPT:MBR 单分区上限 2TB,4TB 盘用 MBR 会浪费一半以上空间
# 为什么必须用 parted/gdisk:CentOS 6 的 fdisk 不支持 GPT
parted /dev/sde
(parted) mklabel gpt # 建立 GPT 分区表(会清除原有分区表,确认 Yes)
(parted) unit TB # 或 unit GB / unit s(扇区)
(parted) print
(parted) mkpart data1 xfs 0% 25% # 用百分比可自动保证对齐(推荐)
(parted) mkpart data2 xfs 25% 100%
(parted) print
(parted) align-check optimal 1 # 输出 "1 aligned" 表示已 4K 对齐
(parted) align-check optimal 2
(parted) quit
# parted 是【即时写入】的,没有 fdisk 那样的 w 确认步骤,操作需格外谨慎
# 非交互式(脚本化,-s 静默)
parted -s /dev/sde mklabel gpt
parted -s /dev/sde mkpart data1 xfs 1MiB 1000GiB
parted -s /dev/sde mkpart data2 xfs 1000GiB 100%
parted -s /dev/sde align-check opt 1
parted -s /dev/sde print
# 用 gdisk 完成同样的事
gdisk /dev/sde
# o → Y 新建空 GPT
# n → 1 → 回车 → +1T → 8300 (8300 = Linux filesystem)
# n → 2 → 回车 → 回车 → 8300
# p 打印
# w → Y 写入
sgdisk -p /dev/sde
sgdisk -n 1:0:+1T -t 1:8300 -c 1:data1 /dev/sde # 非交互式创建
# 验证对齐(手工核对起始扇区)
parted /dev/sde unit s print
# 起始扇区能被 8 整除(512B 扇区)即为 4K 对齐;常见的起始值是 2048
fdisk -l /dev/sde # CentOS 7 的 fdisk 可以只读显示 GPT
blockdev --getpbsz /dev/sde # 物理扇区大小
blockdev --getss /dev/sde # 逻辑扇区大小
cat /sys/block/sde/queue/optimal_io_size
cat /sys/block/sde/queue/minimum_io_size
# 格式化、挂载
partprobe /dev/sde
mkfs.xfs -f -L bigdata1 /dev/sde1
mkfs.xfs -f -L bigdata2 /dev/sde2
mkdir -p /data1 /data2
mount /dev/sde1 /data1; mount /dev/sde2 /data2
df -hT
# GPT 分区表备份(GPT 有主备两份表,但整表备份仍是必要的)
sgdisk --backup=/root/sde-gpt.bak /dev/sde
sgdisk --load-backup=/root/sde-gpt.bak /dev/sde # 恢复
sgdisk -R /dev/sdf /dev/sde && sgdisk -G /dev/sdf # 复制分区表到另一盘并生成新 GUID
dd if=/dev/sde of=/root/sde-gpt-full.bak bs=512 count=34 # 主 GPT 头 + 分区项
练习六:磁盘配额实操(补充)
题目:在 /data(xfs)上为用户 tom 设置 100MB 软限制、150MB 硬限制,宽限期 3 天;并在 ext4 分区上完成同样的配置。
参考答案(xfs,CentOS 7):
# 1. 挂载时启用配额(xfs 的配额只能在挂载时开启,不能事后 quotacheck)
vi /etc/fstab
# /dev/vg_app/lv_web /data xfs defaults,usrquota,grpquota 0 0
# xfs 也可写作 uquota,gquota;项目配额用 pquota
umount /data && mount -a
mount | grep data # 确认选项已生效
xfs_quota -x -c 'state' /data # 查看配额状态
# 2. 设置限制
xfs_quota -x -c 'limit bsoft=100m bhard=150m tom' /data
xfs_quota -x -c 'limit isoft=1000 ihard=1200 tom' /data # inode 数量限制
xfs_quota -x -c 'limit -g bsoft=500m bhard=600m dev' /data # 组配额
# 3. 设置宽限期(xfs 的宽限期是全局的,不能按用户单独设置)
xfs_quota -x -c 'timer -bu 3days' /data
xfs_quota -x -c 'timer -iu 7days' /data
# 4. 查看
xfs_quota -x -c 'report -h' /data
xfs_quota -x -c 'report -ugh' /data
xfs_quota -x -c 'quota -h tom' /data
xfs_quota -x -c 'state -h' /data
# 5. 测试
su - tom
dd if=/dev/zero of=/data/tom/f1 bs=1M count=120 # 超软限制,有警告但可写
dd if=/dev/zero of=/data/tom/f2 bs=1M count=50 # 触碰硬限制,报 Disk quota exceeded
exit
参考答案(ext4,CentOS 6/7 通用):
# 1. 安装工具并启用挂载选项
yum install quota -y
vi /etc/fstab
# /dev/vg_app/lv_db /db ext4 defaults,usrquota,grpquota 0 2
umount /db && mount -a
mount | grep db
# 2. 初始化配额数据库(ext4 独有的步骤,xfs 不需要)
quotacheck -cugv /db # -c 新建 -u 用户 -g 组 -v 详细
# 生成 /db/aquota.user 和 /db/aquota.group
ls -l /db/aquota.*
# 若报 "Cannot remount filesystem read-only",可加 -m 强制
quotacheck -cugvm /db
# 3. 启用配额
quotaon -avug
quotaon -p /db # 查看状态
# 关闭:quotaoff -avug
# 4. 设置限制(交互式编辑,单位是 KB)
edquota -u tom
# Filesystem blocks soft hard inodes soft hard
# /dev/... 0 102400 153600 0 0 0
# (100MB=102400KB,150MB=153600KB)
# 非交互式(脚本友好,推荐)
setquota -u tom 102400 153600 0 0 /db
setquota -u tom 102400 153600 1000 1200 /db # 同时限制 inode
setquota -g dev 512000 614400 0 0 /db
# 5. 设置宽限期
edquota -t
# Block grace period: 3days Inode grace period: 7days
setquota -t 259200 604800 /db # 单位为秒:3天=259200,7天=604800
# 6. 批量复制配额模板(给多个用户设置相同配额的高效做法)
edquota -p tom -u jerry
edquota -p tom -u $(cut -d: -f1 /etc/passwd | awk -F: '$1>=1000')
for u in u1 u2 u3; do edquota -p tom -u $u; done
# 7. 查看与报表
quota -u tom
quota -vs tom # -s 人性化单位
quota -g dev
repquota /db
repquota -a # 所有启用了配额的文件系统
repquota -as # 人性化显示
# 8. 邮件告警
warnquota # 给超限用户发邮件
vi /etc/warnquota.conf # 配置邮件模板与发件人
# 加入 crontab:0 8 * * * /usr/sbin/warnquota
xfs 项目配额(针对目录做配额,用户配额做不到):
# 挂载选项用 pquota(注意 pquota 与 grpquota 互斥,不能同时启用)
# /dev/sdb1 /data xfs defaults,pquota 0 0
mount -o remount,pquota /data
echo "10:/data/project1" >> /etc/projid # 项目 ID : 项目名映射
echo "10:/data/project1" >> /etc/projects # 项目 ID : 目录路径
xfs_quota -x -c 'project -s project1' /data # 初始化项目(-s 递归打标)
xfs_quota -x -c 'limit -p bsoft=1g bhard=1.2g project1' /data
xfs_quota -x -c 'report -p -h' /data
练习七:LUKS 磁盘加密实操(补充)
题目:对 /dev/sdf1 做 LUKS 加密,挂载到 /secure,并配置为使用密钥文件开机自动解锁。
参考答案:
# 0. 确认内核支持并安装工具
yum install cryptsetup -y
modprobe dm_crypt
lsmod | grep dm_crypt
cat /proc/crypto | grep -i aes
# 1. 加密分区(注意:会清除该分区所有数据)
cryptsetup luksFormat /dev/sdf1
# WARNING! ... Are you sure? (Type uppercase yes): YES ← 必须大写 YES
# Enter passphrase: **** (两次)
cryptsetup luksFormat -c aes-xts-plain64 -s 512 -h sha512 /dev/sdf1 # 指定算法
cryptsetup luksDump /dev/sdf1 # 查看头部信息与密钥槽使用情况
blkid /dev/sdf1 # TYPE="crypto_LUKS"
# 2. 打开(映射为 /dev/mapper/secure)
cryptsetup luksOpen /dev/sdf1 secure
# 或 cryptsetup open /dev/sdf1 secure
ls -l /dev/mapper/secure
lsblk /dev/sdf # 可看到 sdf1 下多出 crypt 类型的 secure
dmsetup ls; dmsetup info secure
cryptsetup status secure
# 3. 格式化并挂载(对映射设备操作,不是对 /dev/sdf1)
mkfs.xfs /dev/mapper/secure
mkdir -p /secure
mount /dev/mapper/secure /secure
df -hT /secure
echo "secret" > /secure/secret.txt
# 4. 卸载并关闭
umount /secure
cryptsetup luksClose secure
# 此时 /dev/sdf1 的内容是密文,直接挂载会失败:
mount /dev/sdf1 /mnt # mount: unknown filesystem type 'crypto_LUKS'
# 5. 密钥管理(LUKS 有 8 个密钥槽,支持多个口令)
cryptsetup luksAddKey /dev/sdf1 # 添加新口令
cryptsetup luksChangeKey /dev/sdf1 # 修改口令
cryptsetup luksRemoveKey /dev/sdf1 # 删除某个口令(输入要删的那个)
cryptsetup luksKillSlot /dev/sdf1 1 # 按槽位号删除
cryptsetup luksDump /dev/sdf1 | grep -A2 'Key Slot'
# 6. 使用密钥文件实现开机自动解锁
dd if=/dev/urandom of=/root/secure.key bs=512 count=8
chmod 400 /root/secure.key
cryptsetup luksAddKey /dev/sdf1 /root/secure.key # 把密钥文件加入密钥槽
cryptsetup luksOpen -d /root/secure.key /dev/sdf1 secure # 用密钥文件测试解锁
cryptsetup luksOpen --key-file /root/secure.key /dev/sdf1 secure
# 配置 /etc/crypttab(格式:映射名 底层设备 密钥文件 选项)
blkid /dev/sdf1 # 取 UUID
vi /etc/crypttab
# secure UUID=<sdf1的UUID> /root/secure.key luks
# (密钥文件写 none 或留空则开机时交互询问口令)
chmod 600 /etc/crypttab
# 配置 /etc/fstab(用映射后的设备名)
vi /etc/fstab
# /dev/mapper/secure /secure xfs defaults,_netdev 0 0
# 注意:必须确保 crypttab 先解锁再挂载,加 _netdev 或 nofail 更保险
dracut -f # 重建 initramfs(根分区加密时必做)
systemctl daemon-reload
reboot # 验证自动解锁
# 7. 头部备份(LUKS 头损坏 = 数据永久丢失,此步至关重要)
cryptsetup luksHeaderBackup /dev/sdf1 --header-backup-file /root/sdf1-luks.img
chmod 400 /root/sdf1-luks.img # 头部含密钥材料,必须严格保护并异地保存
cryptsetup luksHeaderRestore /dev/sdf1 --header-backup-file /root/sdf1-luks.img
注意事项:LUKS 口令一旦全部遗忘,数据无法恢复;LUKS 头部(默认 1~2MB)被覆盖同样导致数据永久丢失,必须备份头部;加密会带来 5%~15% 的 CPU 开销(有 AES-NI 指令集时开销很小,用 grep aes /proc/cpuinfo 确认)。
练习八:软件 RAID 5 实操与故障演练(补充)
题目:用 4 块 1GB 分区创建 RAID 5(3 块数据盘 + 1 块热备),格式化挂载后模拟一块盘故障,观察热备自动接管与重建全过程,最后完成换盘。
参考答案:
# ===== 1. 准备 4 个分区(类型 ID 设为 fd,Linux raid autodetect)=====
fdisk /dev/sdb
# n → p → 1 → +1G ... 重复创建 sdb1~sdb4
# t → 1 → fd ... 逐个把类型改为 fd
# w
partprobe /dev/sdb
lsblk; fdisk -l /dev/sdb | grep raid
# ===== 2. 创建 RAID 5 =====
yum install mdadm -y
mdadm -C /dev/md0 -l 5 -n 3 -x 1 /dev/sdb{1,2,3,4}
# 等价长选项写法:
# mdadm --create /dev/md0 --level=5 --raid-devices=3 --spare-devices=1 /dev/sdb{1,2,3,4}
# -C 创建 -l 级别 -n 活动设备数 -x 热备数 -c 指定 chunk 大小(默认 512K)
mdadm -C /dev/md0 -l 5 -n 3 -x 1 -c 64 /dev/sdb{1,2,3,4} # 指定 64K chunk
# ===== 3. 查看状态与同步进度 =====
cat /proc/mdstat
# Personalities : [raid6] [raid5] [raid4]
# md0 : active raid5 sdb3[4] sdb4[3](S) sdb2[1] sdb1[0]
# 2093056 blocks super 1.2 level 5, 512k chunk, algorithm 2 [3/3] [UUU]
# (S)=热备 (F)=故障 [3/3]=应有3块/在线3块 [UUU]=每块都正常(_ 表示缺失)
watch -n 1 cat /proc/mdstat # 实时观察初始同步进度
mdadm -D /dev/md0 # 详细信息(State、Active/Working/Failed/Spare Devices)
mdadm --detail /dev/md0
mdadm -E /dev/sdb1 # 查看单个成员盘的超级块
mdadm -Q /dev/md0 # 简要查询
# ===== 4. 格式化、挂载 =====
mkfs.xfs /dev/md0
mkdir -p /raid5
mount /dev/md0 /raid5
df -hT /raid5
# 容量验证:3 块 1G 的 RAID 5 可用约 2G(N-1),第 4 块作热备不计入容量
cp -r /etc /raid5/ # 写入测试数据
md5sum /raid5/etc/passwd > /root/before.md5
# ===== 5. 保存配置(不做这步重启后阵列名可能变成 /dev/md127)=====
mdadm -Ds > /etc/mdadm.conf
mdadm --detail --scan >> /etc/mdadm.conf
cat /etc/mdadm.conf
# ARRAY /dev/md0 metadata=1.2 spares=1 name=host:0 UUID=xxxx:xxxx:xxxx:xxxx
echo "MAILADDR ops@example.com" >> /etc/mdadm.conf # 故障邮件告警
dracut -f # 若 RAID 承载 / 或 /boot,需重建 initramfs
systemctl enable --now mdmonitor # CentOS 7 启用监控守护进程
service mdmonitor start; chkconfig mdmonitor on # CentOS 6
# fstab 用 UUID 挂载(更稳妥)
blkid /dev/md0
vi /etc/fstab
# UUID=<md0的UUID> /raid5 xfs defaults 0 0
# ===== 6. 模拟故障,观察热备自动接管 =====
mdadm /dev/md0 -f /dev/sdb1 # -f/--fail 标记为故障
# 或 mdadm --manage /dev/md0 --fail /dev/sdb1
cat /proc/mdstat
# md0 : active raid5 sdb4[3] sdb3[4] sdb2[1] sdb1[0](F)
# ... [3/2] [_UU]
# [====>............] recovery = 25.4% (532480/2093056) finish=0.5min speed=48000K/sec
# 观察到:sdb1 变成 (F),热备 sdb4 自动顶上并开始 recovery
watch -n 1 cat /proc/mdstat # 等待重建完成,恢复为 [3/3] [UUU]
mdadm -D /dev/md0 | grep -E 'State|Devices'
# 重建期间业务不中断(降级运行),验证数据仍可读写
md5sum -c /root/before.md5
echo "still writable" > /raid5/test.txt
# 调整重建速度(重建会抢占 I/O,业务高峰期可限速)
cat /proc/sys/dev/raid/speed_limit_min # 默认 1000 (KB/s)
cat /proc/sys/dev/raid/speed_limit_max # 默认 200000
echo 50000 > /proc/sys/dev/raid/speed_limit_min # 提速(加快重建,抢占更多 I/O)
echo 20000 > /proc/sys/dev/raid/speed_limit_max # 限速(保业务)
sysctl -w dev.raid.speed_limit_min=50000 # 永久写入 /etc/sysctl.conf
# ===== 7. 移除坏盘并加入新盘 =====
mdadm /dev/md0 -r /dev/sdb1 # -r/--remove 移除故障盘
cat /proc/mdstat # sdb1 已消失,且当前无热备
mdadm -D /dev/md0
# (物理上换盘、重新分区后)加入新盘作为热备
mdadm /dev/md0 -a /dev/sdb5 # -a/--add 添加
cat /proc/mdstat # sdb5 显示为 (S) 热备
mdadm -D /dev/md0 | grep spare
# 一条命令完成"移除 + 添加"
mdadm /dev/md0 -f /dev/sdb2 -r /dev/sdb2 -a /dev/sdb6
# 更优雅的换盘方式:先复制后剔除(不经历降级状态,风险更低)
mdadm /dev/md0 --replace /dev/sdb2 --with /dev/sdb6
# ===== 8. 主动一致性巡检(发现静默数据损坏)=====
echo check > /sys/block/md0/md/sync_action
cat /sys/block/md0/md/mismatch_cnt # 应为 0;非 0 说明存在校验不一致
echo repair > /sys/block/md0/md/sync_action # 修复不一致
echo idle > /sys/block/md0/md/sync_action # 中止巡检
# ===== 9. 扩容 RAID(增加成员盘扩大容量)=====
mdadm /dev/md0 -a /dev/sdb7
mdadm -G /dev/md0 -n 4 # --grow 把活动设备数从 3 增到 4
watch cat /proc/mdstat # reshape 过程较慢,期间不要断电
xfs_growfs /raid5 # 扩文件系统
mdadm -Ds > /etc/mdadm.conf # 配置变了要重新保存
# ===== 10. 停止与彻底删除 =====
umount /raid5
vi /etc/fstab # 先注释掉相关行,否则重启会失败
mdadm -S /dev/md0 # --stop 停止阵列
cat /proc/mdstat # 已无 md0
mdadm --zero-superblock /dev/sdb1 # 清除成员盘上的 RAID 超级块(关键!否则会被自动重组)
mdadm --zero-superblock /dev/sdb{2,3,4,5,6,7}
wipefs -a /dev/sdb1 # 更彻底地擦除各种文件系统/RAID 签名
rm -f /etc/mdadm.conf
# 重新组装已停止的阵列
mdadm -A /dev/md0 /dev/sdb{1,2,3} # --assemble 手动组装
mdadm -A --scan # 按 /etc/mdadm.conf 自动组装全部
mdadm --assemble --scan --run # 强制运行(即使降级)
思考与要点:
- RAID 5 的容量与冗余:N 块盘容量为 (N-1) 块,允许1 块盘故障;重建期间若第二块盘再故障则数据全失,这是 RAID 5 在大容量盘时代最被诟病的风险,因此重要数据建议 RAID 6 或 RAID 10。
- 热备的价值:故障发生后无需人工介入即刻开始重建,把"降级窗口"从数小时(等运维发现并换盘)缩短到数分钟。
- RAID 不是备份:RAID 只解决硬盘物理故障,无法防止误删除、误格式化、病毒加密、逻辑损坏、机房灾难。备份必须独立存在。
mdadm -Ds > /etc/mdadm.conf必须做,否则重启后阵列可能被识别为/dev/md127,导致 fstab 挂载失败。
练习九:概念快答(补充)
1. MBR 和 GPT 各自的容量与分区数量限制是多少?为什么?
MBR:单分区/单磁盘最大 2TB,最多 4 个主分区(或 3 主 + 1 扩展,扩展分区内可有多个逻辑分区)。原因是 MBR 的 64 字节分区表被 4 个 16 字节表项占满,且每个表项用 4 字节记录起始扇区与总扇区数,2^32 × 512B = 2TB。
GPT:理论支持 8 ZiB(用 8 字节即 64 位记录 LBA),Linux 下默认支持 128 个分区。因为 GPT 头部之后有 128 个 128 字节的分区项区域,且分区项数量可扩展。
2. 为什么推荐用 UUID 而不是 /dev/sdb1 挂载?
设备名 sd* 由内核按磁盘被发现的顺序分配,并不固定。增删硬盘、改变 SATA 插槽、更换 HBA 卡、虚拟机调整磁盘顺序、内核版本变化,都可能让原来的 /dev/sdb1 变成 /dev/sdc1,导致挂载错误的分区或挂载失败。UUID 写在文件系统的超级块里,跟着数据走,唯一且稳定。
3. resize2fs 和 xfs_growfs 的参数有什么区别?
resize2fs 接受设备名(resize2fs /dev/vg/lv),xfs_growfs 接受挂载点(xfs_growfs /data)。此外 resize2fs 可增可减(缩小需卸载),xfs_growfs 只能增大。
4. 硬链接和软链接的区别?为什么硬链接不能跨文件系统?
硬链接是指向同一个 inode 的另一个目录项,与原文件完全平等,删除任一个只是减少链接计数;软链接是一个独立的小文件,内容是目标路径的字符串。inode 号只在单个文件系统内唯一,跨文件系统的 inode 号会冲突,因此硬链接不能跨文件系统,也不能指向目录(会破坏目录树的无环结构)。
5. df 和 du 结果不一致的常见原因?
(1)有已删除但仍被进程占用的文件(lsof +L1);(2)挂载点被覆盖,du 统计的是被隐藏的下层目录;(3)稀疏文件(du 显示实际占用,ls -l 显示逻辑大小);(4)ext 文件系统为 root 预留的 5% 空间;(5)du 无权限读取某些目录导致统计偏小。
6. LVM 中 PE 的大小为什么重要?
PE(Physical Extent)是 LVM 分配空间的最小单位,默认 4MB。LV 的容量必须是 PE 的整数倍,因此 PE 决定了容量调整的粒度。同时在旧版 LVM1 中,单个 LV 最多 65534 个 LE,PE 越小则 LV 上限越小(4MB × 65534 ≈ 256GB)。LVM2 已无此限制,但 PE 过小会增加元数据量、影响大容量 VG 的操作性能。创建后可用 vgchange -s 调整(有限制),因此建议在 vgcreate -s 时就规划好。
7. 为什么 CentOS 7 默认改用 xfs?它有什么代价?
优点:xfs 采用 B+ 树索引与延迟分配,在大文件、大容量(支持 EB 级)、高并发场景下性能显著优于 ext4;inode 动态分配,不会出现 inode 耗尽;mkfs 与 fsck 速度快。代价:不支持缩小;删除大量小文件的性能不如 ext4;早期版本元数据操作较慢(v5 已改善);断电后依赖日志恢复,xfs_repair -L 会丢数据。
8. mount -o remount,rw / 在什么场景下使用?
系统检测到文件系统错误后自动把根分区改为只读保护模式,或在救援环境(rd.break、单用户模式、init=/bin/bash)中根分区默认以只读方式挂载。此时需要该命令把根改为可写才能修改配置文件(如修 fstab)。
附录 A:Linux 运行级别
本节内容属于系统启动与服务管理范畴,因与磁盘救援(单用户模式修 fstab)密切相关而在此保留原文。 详见 阶段02(系统启动流程与服务管理)
Linux 用运行级别(runlevel)表示系统的运行状态,不同级别启动不同的服务集合。
| 运行级别 | 含义 | 说明 |
|---|---|---|
| 0 | 关机(halt) | 系统关闭。不能设为默认级别,否则一开机就关机 |
| 1 | 单用户模式(single user) | 只有 root 可登录,不启动网络与多数服务。用于修复系统、重置 root 密码、修 fstab |
| 2 | 多用户模式,无 NFS | 多用户命令行模式,但不启动 NFS 网络文件系统 |
| 3 | 完整多用户模式(命令行) | 标准的字符界面多用户模式,服务器最常用 |
| 4 | 保留未使用 | 系统保留,用户可自定义 |
| 5 | 图形界面(X11) | 带图形桌面的多用户模式,桌面系统默认 |
| 6 | 重启(reboot) | 系统重启。不能设为默认级别,否则会无限重启 |
CentOS 6 的运行级别管理
配置文件为 /etc/inittab:
# Default runlevel. The runlevels used are:
# 0 - halt (Do NOT set initdefault to this)
# 1 - Single user mode
# 2 - Multiuser, without NFS (The same as 3, if you do not have networking)
# 3 - Full multiuser mode
# 4 - unused
# 5 - X11
# 6 - reboot (Do NOT set initdefault to this)
#
id:5:initdefault:
其中 id:5:initdefault: 表示默认运行级别为 5(图形界面)。把 5 改成 3 则下次开机进入字符界面。
cat /etc/inittab # 查看默认运行级别配置
vi /etc/inittab # 修改 id:5:initdefault: 中的数字
runlevel # 查看当前运行级别
# N 5 ← N 表示上一个级别不存在(开机后未切换过),5 是当前级别
init 3 # 临时切换到运行级别 3(字符界面)
runlevel
# 5 3 ← 上一个级别是 5,当前级别是 3
init 5 # 切回图形界面
init 0 # 关机(等同 halt、poweroff、shutdown -h now)
init 6 # 重启(等同 reboot、shutdown -r now)
init 1 # 切换到单用户模式
startx # 在字符界面下临时启动图形界面(不改变运行级别)
telinit 3 # 与 init 等效
chkconfig --list # 查看各服务在各运行级别下的启停状态
chkconfig --level 35 httpd on # 让 httpd 在级别 3 和 5 下自启动
通过 GRUB 临时修改运行级别(救急手段):开机在 GRUB 菜单界面按 e 编辑启动项,在 kernel 行末尾追加数字或关键字,然后按 b 启动:
kernel /vmlinuz-2.6.32 ro root=/dev/sda1 1 <- 进入单用户模式
kernel /vmlinuz-2.6.32 ro root=/dev/sda1 single <- 同上
kernel /vmlinuz-2.6.32 ro root=/dev/sda1 3 <- 进入字符界面
kernel /vmlinuz-2.6.32 ro root=/dev/sda1 emergency <- 紧急模式
kernel /vmlinuz-2.6.32 ro root=/dev/sda1 init=/bin/bash <- 直接进 shell
这是"误把默认级别设为 0 或 6 导致开不了机"以及"忘记 root 密码"时的标准补救方式。
CentOS 7 的对应关系(systemd target)
CentOS 7 用 systemd 的 target 取代了运行级别,但保留了兼容映射:
| 运行级别 | systemd target | 说明 |
|---|---|---|
| 0 | poweroff.target(runlevel0.target) |
关机 |
| 1 | rescue.target(runlevel1.target) |
救援模式(相当于单用户) |
| 2 | multi-user.target(runlevel2.target) |
多用户 |
| 3 | multi-user.target(runlevel3.target) |
多用户命令行 |
| 4 | multi-user.target(runlevel4.target) |
多用户 |
| 5 | graphical.target(runlevel5.target) |
图形界面 |
| 6 | reboot.target(runlevel6.target) |
重启 |
| — | emergency.target |
紧急模式,只挂载根文件系统且为只读 |
systemctl get-default # 查看默认 target
systemctl set-default multi-user.target # 设为字符界面(等同原级别 3)
systemctl set-default graphical.target # 设为图形界面(等同原级别 5)
# 本质是创建符号链接:
ls -l /etc/systemd/system/default.target
ln -sf /lib/systemd/system/multi-user.target /etc/systemd/system/default.target
systemctl isolate multi-user.target # 临时切换(等同 init 3)
systemctl isolate graphical.target # 等同 init 5
systemctl isolate rescue.target # 等同 init 1
systemctl rescue # 进入救援模式
systemctl emergency # 进入紧急模式
runlevel # 仍然可用(兼容命令)
init 3 # 仍然可用,被映射到对应 target
who -r # 查看运行级别
systemctl list-units --type=target # 列出当前已激活的 target
systemctl list-dependencies multi-user.target # 查看 target 的依赖服务树
/etc/inittab在 CentOS 7 中仍然存在,但只是一个说明文件,修改它不起任何作用,文件内明确写着 "inittab is no longer used"。
附录 B:Linux 文件系统目录结构
本节列出根目录下各子目录的用途,与文件系统概念、分区规划直接相关。 详见 阶段01(Linux 基础与文件目录管理)
Linux 采用单一的树形目录结构,一切从根 / 开始,所有分区、外部设备都以"挂载"的形式挂在这棵树的某个节点上,没有 Windows 的 C:、D: 盘符概念。
| 目录 | 用途说明 |
|---|---|
/ |
根目录,整个文件系统树的起点,所有目录和文件都在其下 |
/bin |
基本命令的二进制文件(binary),所有用户都可执行,如 ls、cp、mv、cat、bash。系统启动和单用户模式修复时必需,因此不能单独分区 |
/sbin |
系统管理命令(system binary),主要供 root 使用,如 fdisk、mkfs、ifconfig、init、reboot。同样不能单独分区 |
/boot |
内核与引导文件,存放 vmlinuz(内核)、initramfs(初始内存文件系统)、GRUB 配置。常单独分一个 200MB~1GB 的分区 |
/dev |
设备文件目录(device),Linux 把所有硬件抽象为文件,如 /dev/sda、/dev/null、/dev/tty。由 udev 动态管理,实际是 devtmpfs 内存文件系统 |
/etc |
系统与应用的配置文件(etcetera),如 /etc/fstab、/etc/passwd、/etc/inittab、/etc/hosts。绝不能单独分区(启动时就要读取),且是备份的重中之重 |
/home |
普通用户的家目录,每个用户对应一个子目录如 /home/tom。用户数据集中地,生产环境建议单独分区,便于配额管理和防止撑爆根分区 |
/root |
root 用户的家目录(注意与根目录 / 区分)。放在根分区上,保证单用户模式下可用 |
/lib |
共享库文件(library),程序运行所依赖的动态链接库(.so)以及内核模块(/lib/modules)。不能单独分区 |
/lib64 |
64 位系统的 64 位共享库 |
/media |
可移动介质的挂载点,U 盘、光驱、移动硬盘插入后由桌面环境自动挂载到此 |
/mnt |
临时挂载点,管理员手工挂载文件系统时的习惯用目录 |
/opt |
第三方大型软件的安装目录(optional),如 Oracle、MATLAB、自研应用。软件较多时可单独分区 |
/proc |
虚拟文件系统(procfs),不占磁盘空间,是内核与进程信息在内存中的映射,如 /proc/cpuinfo、/proc/meminfo、/proc/partitions、/proc/mdstat、/proc/<pid>/ |
/sys |
虚拟文件系统(sysfs),导出内核的设备与驱动信息,如 /sys/block/sda/、/sys/class/scsi_host/。磁盘热插拔扫描就靠它 |
/run |
运行时数据(tmpfs 内存文件系统),存放 PID 文件、socket 等,每次开机重建。CentOS 7 引入,取代部分 /var/run |
/srv |
服务数据目录(service),存放网络服务对外提供的数据,如 Web、FTP 的数据(实际用得较少) |
/tmp |
临时文件目录,所有用户可读写(带 sticky 位),系统重启或定期清理。建议单独分区并加 nodev,nosuid,noexec 选项以提升安全性 |
/usr |
应用程序与文件的主要存放地(Unix Software Resource),是系统中最大的目录之一。包含 /usr/bin、/usr/sbin、/usr/lib、/usr/local、/usr/share(文档、man 手册)、/usr/src(源码)、/usr/include(头文件)。类似 Windows 的 Program Files |
/usr/local |
管理员手工编译安装的软件存放地,与包管理器安装的内容隔开,便于维护 |
/var |
经常变化的数据(variable),如日志 /var/log、邮件 /var/spool/mail、打印队列、数据库文件 /var/lib/mysql、缓存 /var/cache。最容易膨胀,生产环境强烈建议单独分区 |
/var/log |
系统与应用日志,如 messages、secure、dmesg、boot.log。日志暴涨撑满根分区是最常见的故障 |
/lost+found |
ext 文件系统专有,fsck 修复时把找到的孤立文件(inode 存在但无目录项)放在此处。每个 ext 分区的根下都有一个 |
/selinux |
SELinux 相关的虚拟文件系统(CentOS 6;CentOS 7 移到 /sys/fs/selinux) |
Windows 与 Linux 文件系统结构对比:
Windows:多根结构(每个分区一个盘符)
C:\ D:\ E:\
├─Windows ├─data └─backup
├─Users └─software
└─Program Files
Linux:单根结构(所有分区挂载到同一棵树上)
/
├── bin ← 根分区
├── etc ← 根分区
├── boot ← 独立分区 /dev/sda1
├── home ← 独立分区 /dev/sda3(或 LV)
├── var ← 独立分区 /dev/sda5(或 LV)
├── data ← 独立分区 /dev/sdb1
└── mnt
└── usb ← U 盘挂载点 /dev/sdc1
文件名规则:Linux 文件名最长 255 个字符,路径全长最长 4096 字符;区分大小写(File.txt 与 file.txt 是两个不同的文件);除 /(路径分隔符)和 \0 外几乎所有字符都可用,但应避免空格、*、?、$、- 开头等会引起 shell 解析问题的字符;以 . 开头的文件为隐藏文件(ls -a 可见)。
目录与分区规划的关系:
| 目录 | 能否单独分区 | 原因 |
|---|---|---|
/bin、/sbin、/lib、/lib64、/etc、/dev |
❌ 绝对不能 | 系统启动早期就必须访问,此时其它分区尚未挂载 |
/boot |
✅ 推荐 | 隔离内核文件;旧 BIOS 有 1024 柱面限制;UEFI 需要 ESP |
/home |
✅ 推荐 | 隔离用户数据,便于配额和重装系统时保留数据 |
/var、/var/log |
✅ 强烈推荐 | 日志与数据库最易膨胀,隔离后不会撑爆根分区 |
/tmp |
✅ 推荐 | 防止临时文件占满根分区,可加安全挂载选项 |
/usr |
⚠️ 可以但需谨慎 | 需要在 initramfs 中正确处理,配置复杂,一般不单独分 |
/opt、/data、/srv |
✅ 可以 | 应用与业务数据,按需规划 |
附录 C:GRUB 启动引导
本节内容属于系统启动引导范畴,因与 MBR、
/boot分区、分区表恢复密切相关而在此完整保留原文。 详见 阶段02(系统启动流程与引导修复)
C.1 引导程序的种类
| 引导程序 | 适用范围 | 说明 |
|---|---|---|
| grub (GRUB Legacy / grub 0.97) | CentOS 5/6、RHEL 5/6 | 配置文件 /boot/grub/grub.conf(/boot/grub/menu.lst 为其符号链接),语法用 title/root (hd0,0)/kernel/initrd |
| grub2 | CentOS 7+、Ubuntu 10+ | 配置文件 /boot/grub2/grub.cfg,语法用 menuentry/set root/linux16/initrd16,功能强大但配置文件不建议手工编辑(由脚本生成) |
| grub4dos | Windows 下的多重引导 | 常用于 U 盘启动盘、从硬盘安装系统、加载 memdisk 引导镜像 |
| lilo | 早期 Linux | 已基本淘汰 |
| syslinux / isolinux / pxelinux | U 盘 / 光盘 / 网络启动 | 轻量引导器,memdisk 属于该家族 |
| bootmgr / NTLDR | Windows | Windows 自己的引导管理器 |
C.2 GRUB 的磁盘与分区命名方式
GRUB 有一套独立于 Linux 的设备命名法,且从 0 开始计数:
grub (GRUB Legacy):
(hd0) 第 1 块硬盘
(hd0,0) 第 1 块硬盘的第 1 个分区 → 对应 /dev/sda1
(hd0,1) 第 1 块硬盘的第 2 个分区 → 对应 /dev/sda2
(hd1,4) 第 2 块硬盘的第 5 个分区 → 对应 /dev/sdb5(第一个逻辑分区)
(fd0) 软驱
(cd) 光驱
grub2:
(hd0,msdos1) 第 1 块硬盘的 MBR 第 1 个分区 → /dev/sda1
(hd0,gpt1) 第 1 块硬盘的 GPT 第 1 个分区
(hd0,1) 简写形式,grub2 中【分区从 1 开始编号】(与 GRUB Legacy 不同!)
(hd1,msdos5) 第 2 块硬盘的第 1 个逻辑分区 → /dev/sdb5
关键差异:GRUB Legacy 的分区号从 0 开始,grub2 的分区号从 1 开始,磁盘号两者都从 0 开始。这是修改配置时最容易出错的地方。
# 在 grub 命令行中探查设备
grub> ls # 列出所有设备与分区
grub> ls (hd0,msdos1)/ # 列出该分区根目录的内容
grub> ls (hd0,msdos1)/boot # 确认内核文件在哪个分区
grub> cat (hd0,msdos1)/etc/fstab # 直接查看文件内容
grub> set # 显示所有变量
grub> echo $prefix
grub> insmod ls
C.3 grub2 配置文件与常用指令
/boot/grub2/grub.cfg 的核心指令:
set default="0" # 默认启动第 0 个菜单项
set timeout=5 # 菜单等待 5 秒
set root=(hd0,msdos1) # 设置 GRUB 的根(内核文件所在的分区)
menuentry 'CentOS Linux 7' { # 一个启动菜单项
insmod part_msdos # 加载 MBR 分区支持模块
insmod xfs # 加载 xfs 文件系统模块(读 /boot 用)
insmod ext2 # 加载 ext2/3/4 支持
insmod gzio # 解压支持
insmod lvm # LVM 支持(/boot 不能在 LVM 上,但根可以)
insmod mdraid09 # 软 RAID 支持
set root='hd0,msdos1'
search --no-floppy --fs-uuid --set=root 1a2b3c4d-... # 按 UUID 定位分区
linux16 /vmlinuz-3.10.0-1160.el7.x86_64 root=/dev/mapper/centos-root ro quiet
initrd16 /initramfs-3.10.0-1160.el7.x86_64.img
}
各指令含义:
| 指令 | 作用 |
|---|---|
set root= |
指定 GRUB 去哪个分区找内核文件(不是 Linux 的根文件系统) |
search --fs-uuid --set=root |
按文件系统 UUID 查找分区并设为 root,比写死设备名更可靠 |
linux / linux16 / linuxefi |
加载内核。linux16 用 16 位实模式引导(BIOS),linuxefi 用于 UEFI |
initrd / initrd16 / initrdefi |
加载初始内存文件系统 |
root=/dev/xxx |
内核参数,告诉内核真正的根文件系统在哪(这个才决定 Linux 的 /) |
ro / rw |
根文件系统初始以只读/读写方式挂载 |
insmod |
加载 GRUB 模块(分区格式、文件系统、加密、LVM、RAID 等) |
boot |
执行启动(在交互命令行中手工输入时必须以此结束) |
chainloader +1 |
链式加载,把控制权交给该分区的引导记录(用于引导 Windows) |
set gfxpayload=keep |
保持图形模式 |
关机与重启菜单项:
menuentry "Reboot" {
reboot
}
menuentry "Shutdown" {
halt
}
menuentry "Firmware Setup (UEFI)" {
fwsetup
}
C.4 grub 交互命令行手工引导(救急)
当 grub.cfg 损坏或配置错误时,GRUB 会掉到命令行提示符。有两种提示符:
grub>:GRUB 已完整加载(能找到/boot/grub2下的模块),功能齐全;grub rescue>:GRUB 只加载了最基础的部分(找不到normal.mod等模块),需要先设置prefix并加载模块。
在 grub> 下手工引导 CentOS 7:
grub> ls # 找出分区
grub> ls (hd0,msdos1)/ # 确认哪个分区有 vmlinuz
grub> set root=(hd0,msdos1)
grub> linux16 /vmlinuz-3.10.0-1160.el7.x86_64 root=/dev/mapper/centos-root ro
grub> initrd16 /initramfs-3.10.0-1160.el7.x86_64.img
grub> boot
如果
/boot是独立分区,内核路径就是/vmlinuz-...;如果/boot在根分区上,路径要写成/boot/vmlinuz-...。这是最常见的失败原因。
在 grub rescue> 下恢复:
grub rescue> ls
grub rescue> ls (hd0,msdos1)/boot/grub2 # 找到 grub2 目录所在
grub rescue> set prefix=(hd0,msdos1)/boot/grub2
grub rescue> set root=(hd0,msdos1)
grub rescue> insmod normal
grub rescue> normal # 切换到完整的 grub> 环境
grub> ... # 之后按上面的方法手工引导
在 GRUB Legacy(CentOS 6)中手工引导:
grub> root (hd0,0)
grub> kernel /vmlinuz-2.6.32-754.el6.x86_64 ro root=/dev/sda2
grub> initrd /initramfs-2.6.32-754.el6.x86_64.img
grub> boot
进入系统后必须做的事:手工引导只是一次性的,进入系统后要重新生成配置并重装引导。
# CentOS 7
grub2-mkconfig -o /boot/grub2/grub.cfg # 重新生成配置(BIOS)
grub2-mkconfig -o /boot/efi/EFI/centos/grub.cfg # UEFI
grub2-install /dev/sda # 重装引导程序到 MBR(BIOS 才需要)
# CentOS 6
grub-install /dev/sda
vi /boot/grub/grub.conf # CentOS 6 直接手工编辑
# Ubuntu/Debian
update-grub
grub-install /dev/sda
C.5 /boot 分区的内容
ls -l /boot
CentOS 7 的 /boot:
config-3.10.0-1160.el7.x86_64 内核编译时的配置项
grub2/ grub2 的配置与模块
grub.cfg 主配置文件
grubenv 保存 saved_entry 等环境变量
i386-pc/ BIOS 平台的模块(*.mod)
initramfs-3.10.0-1160.el7.x86_64.img 初始内存文件系统(含驱动,用于挂载真正的根)
initrd-plymouth.img 启动画面
symvers-*.gz 内核符号表
System.map-3.10.0-1160.el7.x86_64 内核符号地址映射
vmlinuz-3.10.0-1160.el7.x86_64 【内核本体】(压缩的可引导内核)
efi/ UEFI 引导文件(UEFI 系统)
loader/ systemd-boot 相关
CentOS 6 的 /boot:
grub/
grub.conf 主配置文件
menu.lst -> grub.conf 符号链接
device.map GRUB 设备名与 Linux 设备名的映射
stage1 / stage2 / *_stage1_5 GRUB 的各阶段引导代码
vmlinuz-2.6.32-754.el6.x86_64
initramfs-2.6.32-754.el6.x86_64.img
为什么 /boot 常单独分区:
- 早期 BIOS 只能访问硬盘前 1024 柱面(约 8GB),内核必须放在这个范围内;
- 根分区使用 LVM、RAID 5/6、加密、btrfs 等复杂格式时,引导程序可能无法读取,而
/boot用简单的 ext4/xfs 可确保被读到(/boot不能放在 LVM 上,grub2 虽支持但不推荐); - 便于隔离与备份,也方便多系统共用;
- UEFI 必须有一个 ESP(EFI System Partition),FAT32 格式,通常挂载在
/boot/efi,大小 200~500MB。
/boot 满了的处理(内核升级失败的最常见原因):
df -h /boot
rpm -qa kernel | sort -V # 列出已安装的内核
uname -r # 当前正在用的内核(绝对不能删)
package-cleanup --oldkernels --count=2 # 保留 2 个最新内核,删除其余(yum-utils 包)
yum remove kernel-3.10.0-862.el7 # 手工删除指定旧内核
# 永久限制保留数量:/etc/yum.conf 中设置 installonly_limit=2
grub2-mkconfig -o /boot/grub2/grub.cfg # 删完重新生成菜单
C.6 引导修复:chroot 方式
系统装了 Windows 后 MBR 被覆盖、或误删了 GRUB,标准修复流程是用光盘/U 盘启动到救援环境,chroot 进原系统后重装引导。
# ===== CentOS 7 =====
# 1. 用安装盘启动 → Troubleshooting → Rescue a CentOS system → 1) Continue
# 安装程序会自动把原系统挂载到 /mnt/sysimage
chroot /mnt/sysimage
# 2. 重装引导并重建配置
grub2-install /dev/sda # BIOS 模式,写入 MBR
grub2-mkconfig -o /boot/grub2/grub.cfg
# UEFI 模式(不需要 grub2-install,只需重装包并重建配置)
yum reinstall grub2-efi grub2-efi-modules shim -y
grub2-mkconfig -o /boot/efi/EFI/centos/grub.cfg
# 3. 若 initramfs 也损坏
dracut -f --kver $(uname -r)
dracut -f /boot/initramfs-$(uname -r).img $(uname -r)
# 4. 退出重启
exit
reboot
# ===== 手工挂载的通用做法(用任意 Live CD)=====
mkdir /mnt/sysroot
mount /dev/mapper/centos-root /mnt/sysroot # 先挂根
mount /dev/sda1 /mnt/sysroot/boot # 再挂 /boot
mount /dev/sda2 /mnt/sysroot/boot/efi # UEFI 还要挂 ESP
mount --bind /dev /mnt/sysroot/dev # 关键:绑定这四个虚拟文件系统
mount --bind /proc /mnt/sysroot/proc
mount --bind /sys /mnt/sysroot/sys
mount --bind /run /mnt/sysroot/run
chroot /mnt/sysroot
grub2-install /dev/sda && grub2-mkconfig -o /boot/grub2/grub.cfg
exit
umount -R /mnt/sysroot
reboot
# ===== CentOS 6 =====
chroot /mnt/sysimage
grub-install /dev/sda
# 或在 grub 交互命令行中:
grub
grub> root (hd0,0)
grub> setup (hd0) # 把 GRUB 写入 hd0 的 MBR
grub> quit
C.7 多系统引导配置示例
一台机器同时装了 CentOS 7 与 Windows 7 时的 grub.cfg 片段:
set default=0
set timeout=10
set gfxpayload=keep
### Linux 启动项 ###
menuentry 'CentOS Linux 7 (3.10.0-1160.el7.x86_64)' {
load_video
set gfxpayload=keep
insmod gzio
insmod part_msdos
insmod xfs
set root='hd0,msdos1'
search --no-floppy --fs-uuid --set=root 1a2b3c4d-5e6f-7a8b-9c0d-1e2f3a4b5c6d
linux16 /vmlinuz-3.10.0-1160.el7.x86_64 root=/dev/mapper/centos-root ro rhgb quiet
initrd16 /initramfs-3.10.0-1160.el7.x86_64.img
}
### Windows 启动项(用 chainloader 链式加载)###
menuentry "Windows 7" {
insmod part_msdos
insmod ntfs
insmod ntldr
set root='hd0,msdos3'
search --no-floppy --fs-uuid --set=root --hint='hd0,msdos3' 1A2B3C4D5E6F7A8B
ntldr /bootmgr
}
### 另一种 Windows 写法 ###
menuentry "Windows 7 (chainloader)" {
insmod chain
insmod part_msdos
insmod ntfs
set root=(hd0,msdos3)
chainloader +1
}
### UEFI 下的 Windows ###
menuentry "Windows Boot Manager (UEFI)" {
insmod part_gpt
insmod fat
insmod chain
set root=(hd0,gpt1)
chainloader /EFI/Microsoft/Boot/bootmgfw.efi
}
### 关机与重启 ###
menuentry "Reboot" { reboot }
menuentry "Shutdown" { halt }
自动探测其它系统(比手写更省事):
yum install os-prober -y # 安装后 grub2-mkconfig 会自动探测 Windows
os-prober # 手工执行看能探测到什么
grub2-mkconfig -o /boot/grub2/grub.cfg
注意:先装 Windows 再装 Linux 更省事,因为 Windows 安装程序会无条件覆盖 MBR 并且不认识 Linux;Linux 的 GRUB 则能引导 Windows。若顺序反了,需按 C.6 修复引导。
C.8 修改默认启动项与菜单持久化
# 1. 查看当前所有菜单项及其序号
grep -E '^menuentry' /boot/grub2/grub.cfg | cut -d"'" -f2
awk -F\' '/^menuentry/ {print i++ " : " $2}' /boot/grub2/grub.cfg
grubby --default-index # 当前默认项序号
grubby --default-kernel # 当前默认内核
grubby --info=ALL # 全部启动项详情
# 2. 按序号设置默认启动项
grub2-set-default 0 # 第 1 项
grub2-set-default 2
# 按标题设置(更直观,标题必须与菜单里的完全一致)
grub2-set-default "Windows 7"
grub2-set-default "CentOS Linux 7 (3.10.0-1160.el7.x86_64)"
# 3. 查看设置结果(存放在 /boot/grub2/grubenv 中)
grub2-editenv list
# saved_entry=Windows 7
cat /boot/grub2/grubenv
grub2-editenv - unset saved_entry # 清除
# 4. 只让【下一次】启动进入某项(一次性,重启后恢复原默认)
grub2-reboot 1
grub2-reboot "Windows 7"
grub2-editenv - set next_entry=1
# 5. 让 saved_entry 生效的前提:/etc/default/grub 中必须是 saved
vi /etc/default/grub
GRUB_TIMEOUT=5
GRUB_DEFAULT=saved
GRUB_DISABLE_SUBMENU=true
GRUB_TERMINAL_OUTPUT="console"
GRUB_CMDLINE_LINUX="crashkernel=auto rhgb quiet"
GRUB_DISABLE_RECOVERY="true"
GRUB_SAVEDEFAULT=true
# 改完 /etc/default/grub 后必须重新生成 grub.cfg 才生效
grub2-mkconfig -o /boot/grub2/grub.cfg # BIOS
grub2-mkconfig -o /boot/efi/EFI/centos/grub.cfg # UEFI
GRUB_DEFAULT 的取值:
| 取值 | 含义 |
|---|---|
0、1、2 … |
固定按序号启动第 N 项 |
saved |
使用 grub2-set-default 保存的值(配合 grub2-set-default 使用时必须设为此值) |
"CentOS Linux 7 ..." |
按菜单标题固定 |
>0>1 |
子菜单中的项(第 0 个子菜单下的第 1 项) |
自定义菜单项的持久化:/etc/grub.d/40_custom
直接编辑 /boot/grub2/grub.cfg 添加的菜单项,会在下次执行 grub2-mkconfig(如内核升级时自动触发)后被完全覆盖丢失。要让自定义项持久保留,必须写入 /etc/grub.d/40_custom:
vi /etc/grub.d/40_custom
#!/bin/sh
exec tail -n +3 $0
# 这一行以上的内容不要修改
# 下面添加自定义菜单项,它们会被原样追加到 grub.cfg 的末尾
menuentry "Windows 7" {
insmod part_msdos
insmod ntfs
set root=(hd0,msdos3)
chainloader +1
}
menuentry "Reboot" { reboot }
menuentry "Shutdown" { halt }
# 【关键】该文件必须有可执行权限,否则 grub2-mkconfig 会跳过它
chmod +x /etc/grub.d/40_custom
ls -l /etc/grub.d/ # 所有脚本都应是 755
grub2-mkconfig -o /boot/grub2/grub.cfg # 重新生成
grep -A5 'Windows 7' /boot/grub2/grub.cfg # 验证已写入
/etc/grub.d/ 下各脚本的作用(按文件名数字顺序执行):
| 脚本 | 作用 |
|---|---|
00_header |
生成头部:timeout、default、显示模式等 |
01_users |
GRUB 菜单密码保护相关 |
10_linux |
扫描 /boot 下的内核,生成 Linux 启动项 |
20_linux_xen |
Xen 虚拟化启动项 |
20_ppc_terminfo |
PowerPC 终端 |
30_os-prober |
探测其它操作系统(Windows 等)并生成启动项 |
40_custom |
用户自定义菜单项(推荐在此添加) |
41_custom |
读取 /boot/grub2/custom.cfg 的内容 |
给 GRUB 菜单加密码保护(防止他人用单用户模式绕过 root 密码):
grub2-mkpasswd-pbkdf2 # 输入密码,得到 grub.pbkdf2.sha512.xxx 串
vi /etc/grub.d/40_custom
# set superusers="admin"
# password_pbkdf2 admin grub.pbkdf2.sha512.10000.XXXX...
grub2-mkconfig -o /boot/grub2/grub.cfg
# CentOS 6 的做法:
grub-md5-crypt # 生成 md5 串
# 在 /boot/grub/grub.conf 的 title 行之前加:password --md5 $1$xxxx
C.9 BIOS 与 UEFI 的差异对照
| 项目 | BIOS(Legacy) | UEFI |
|---|---|---|
| 分区表 | 通常搭配 MBR(也可 GPT) | 必须搭配 GPT |
| 引导代码位置 | 硬盘的 MBR 前 446 字节 + 后续扇区 | ESP 分区(FAT32)内的 .efi 文件 |
| 磁盘容量上限 | MBR 限制 2TB | 8 ZiB |
| grub2 配置文件路径 | /boot/grub2/grub.cfg |
/boot/efi/EFI/centos/grub.cfg |
| grub2 模块目录 | /boot/grub2/i386-pc/ |
/boot/efi/EFI/centos/ + /usr/lib/grub/x86_64-efi/ |
| 内核加载指令 | linux16 / initrd16 |
linuxefi / initrdefi |
| 安装引导命令 | grub2-install /dev/sda |
不用 grub2-install,重装 grub2-efi+shim 包后 grub2-mkconfig |
| 必需的特殊分区 | GPT+BIOS 时需 BIOS Boot Partition(1~2MB,类型 ef02,无文件系统) |
ESP(200~500MB,FAT32,类型 ef00,挂载 /boot/efi) |
| 安全启动 | 不支持 | 支持 Secure Boot(需 shim 签名) |
| 启动项管理 | 只能靠引导程序 | 固件中有 NVRAM 启动项,用 efibootmgr 管理 |
# 判断当前系统是 BIOS 还是 UEFI 启动
[ -d /sys/firmware/efi ] && echo "UEFI" || echo "BIOS"
ls /sys/firmware/efi
dmesg | grep -i efi
efibootmgr -v # UEFI 下列出固件启动项
# UEFI 启动项管理
efibootmgr -v # 查看
efibootmgr -o 0002,0000,0001 # 调整启动顺序
efibootmgr -b 0003 -B # 删除编号为 0003 的启动项
efibootmgr -c -d /dev/sda -p 1 -L "CentOS" -l '\EFI\centos\shimx64.efi' # 新建启动项
# ESP 分区的典型内容
ls -R /boot/efi/EFI/
# EFI/BOOT/BOOTX64.EFI 通用后备引导文件
# EFI/centos/shimx64.efi 签名的第一阶段引导(Secure Boot 用)
# EFI/centos/grubx64.efi grub2 主体
# EFI/centos/grub.cfg grub2 配置
# EFI/Microsoft/Boot/bootmgfw.efi Windows 引导管理器
GPT + BIOS 的特殊要求:在 BIOS 模式下用 GPT 分区表引导 Linux,必须额外创建一个 1~2MB 的 BIOS Boot Partition(
parted中设set N bios_grub on,gdisk中类型码ef02),用来存放 GRUB 的 core.img。否则grub2-install会报 "embedding area is unusually small" 而失败。
# 用 parted 创建 BIOS Boot Partition
parted /dev/sda mkpart primary 1MiB 3MiB
parted /dev/sda set 1 bios_grub on
parted /dev/sda print # Flags 列显示 bios_grub
# 用 sgdisk
sgdisk -n 1:2048:+2M -t 1:ef02 -c 1:"BIOS boot" /dev/sda
附录 D:从硬盘安装操作系统
本节内容属于系统安装范畴,因涉及分区、引导、loop 挂载而在此完整保留原文。 详见 阶段01(Linux 系统安装与初始化)
在没有光驱、没有 U 盘的情况下,可以把安装镜像(ISO)放在硬盘的某个分区上,通过 GRUB 直接引导安装程序。这是运维现场常用的技巧。
通用前提条件:
- ISO 文件放在一个能被引导程序读取的分区上(ext4、xfs、NTFS、FAT32 均可,FAT32 注意单文件 4GB 上限);
- 记住该分区在 GRUB 中的编号(
(hd0,msdos1))和在 Linux 中的设备名(/dev/sda1); - 从 ISO 中把
vmlinuz(内核) 和initrd.img(初始内存文件系统) 提取到/boot或该分区根目录,因为 GRUB 需要先加载这两个文件才能启动安装程序; - 安装目标分区不能是存放 ISO 的那个分区(会被格式化)。
D.1 安装 CentOS 7
# 1. 把 ISO 放到某个分区,例如 /dev/sda1 的根目录
cp CentOS-7-x86_64-DVD-2009.iso /boot/ # 或放在 /dev/sda1 挂载点的根下
# 2. 从 ISO 中提取内核与 initrd
mount -o loop CentOS-7-x86_64-DVD-2009.iso /mnt
ls /mnt/isolinux/ # vmlinuz、initrd.img 在这里
cp /mnt/isolinux/vmlinuz /boot/vmlinuz-install
cp /mnt/isolinux/initrd.img /boot/initrd-install.img
umount /mnt
# 3. 添加 GRUB 菜单项
vi /etc/grub.d/40_custom
menuentry "Install CentOS 7 from HDD" {
set root=(hd0,msdos1)
linux /vmlinuz-install repo=hd:/dev/sda1:/ quiet
initrd /initrd-install.img
}
chmod +x /etc/grub.d/40_custom
grub2-mkconfig -o /boot/grub2/grub.cfg
reboot # 重启后选择该菜单项
关键内核参数 repo=(CentOS 7):
repo=hd:/dev/sda1:/ ISO 位于 /dev/sda1 分区的【根目录】
repo=hd:/dev/sda1:/iso/ ISO 位于 /dev/sda1 的 iso 子目录
repo=hd:UUID=xxxx-xxxx:/ 用 UUID 指定分区(设备名不确定时更可靠)
repo=hd:LABEL=DATA:/ 用卷标指定
inst.repo=hd:/dev/sda1:/ CentOS 7.x 新写法(inst. 前缀)
inst.stage2=hd:/dev/sda1:/ 指定 stage2 镜像位置
inst.ks=hd:/dev/sda1:/ks.cfg 自动化安装的 kickstart 文件
linux dd 进入驱动加载模式
CentOS 6 的对应参数写法为
repo=hd:/dev/sda1:/或旧式的method=hd:/dev/sda1:/;CentOS 6 在安装界面中也可选择 "Hard drive" 作为安装源,然后手工指定分区和目录。
D.2 安装 Linux Mint 19 / Ubuntu(loopback 方式)
Debian 系的安装镜像使用 casper(Live 系统),参数与 CentOS 不同,用 iso-scan/filename 指定 ISO 路径。
# 1. 把 ISO 放到分区上,例如 /dev/sda1 的根目录
# 2. 从 ISO 中提取 casper 目录下的内核与 initrd
mount -o loop linuxmint-19-cinnamon-64bit.iso /mnt
ls /mnt/casper/ # vmlinuz、initrd.lz
cp /mnt/casper/vmlinuz /boot/vmlinuz-mint
cp /mnt/casper/initrd.lz /boot/initrd-mint.lz
umount /mnt
# 3. GRUB 菜单项(用 loopback 模块直接把 ISO 当作设备挂载)
vi /etc/grub.d/40_custom
menuentry "Install Linux Mint 19 from HDD" {
insmod part_msdos
insmod ext2
insmod loopback
insmod iso9660
set isofile="/linuxmint-19-cinnamon-64bit.iso"
set root=(hd0,msdos1)
loopback loop $isofile
linux (loop)/casper/vmlinuz boot=casper iso-scan/filename=$isofile noeject noprompt splash --
initrd (loop)/casper/initrd.lz
}
chmod +x /etc/grub.d/40_custom
grub2-mkconfig -o /boot/grub2/grub.cfg
关键参数说明:
| 参数 | 作用 |
|---|---|
loopback loop $isofile |
把 ISO 文件映射为 GRUB 的 (loop) 设备,之后可直接读取其内部文件 |
boot=casper |
告诉 initrd 使用 casper 引导 Live 系统 |
iso-scan/filename= |
最关键:让 casper 在各分区上搜索这个 ISO 文件并挂载为安装源 |
noeject noprompt |
安装结束时不提示弹出介质 |
splash |
显示启动画面 |
-- |
参数结束标记 |
Ubuntu 的 ISO 内核路径可能是
/casper/vmlinuz.efi、initrd 可能是/casper/initrd(不带.lz),需先用ls (loop)/casper/在 GRUB 命令行中确认实际文件名。
D.3 安装 Windows 7(memdisk + ghost 镜像方式)
Windows 的安装程序无法像 Linux 那样直接从 ISO 引导,需要借助 memdisk(syslinux 家族的工具)把镜像整体加载到内存中模拟为软驱/硬盘,或使用 ghost 镜像还原。
# 1. 准备文件(放在 /boot 或某个分区根目录)
# memdisk —— 来自 syslinux 包:/usr/share/syslinux/memdisk
# ghost.img —— 制作好的 DOS 启动镜像,内含 Ghost 程序
# win7.gho —— Windows 7 的 ghost 备份文件
yum install syslinux -y
cp /usr/share/syslinux/memdisk /boot/
cp ghost.img /boot/
# win7.gho 放在数据分区上,如 /dev/sda5 的根目录
# 2. GRUB 菜单项:用 memdisk 加载 DOS 镜像
vi /etc/grub.d/40_custom
menuentry "Ghost for Windows 7 (memdisk)" {
insmod part_msdos
insmod ext2
set root=(hd0,msdos1)
linux16 /memdisk
initrd16 /ghost.img
}
menuentry "DOS Tools (memdisk floppy image)" {
set root=(hd0,msdos1)
linux16 /memdisk floppy
initrd16 /dos.img
}
menuentry "Windows 7 Installer (bootmgr)" {
insmod ntfs
set root=(hd0,msdos3)
ntldr /bootmgr
}
chmod +x /etc/grub.d/40_custom
grub2-mkconfig -o /boot/grub2/grub.cfg
memdisk 的常用参数:
| 参数 | 作用 |
|---|---|
floppy |
把镜像模拟为软驱(A:),适合 1.44MB / 2.88MB 的 DOS 启动盘 |
harddisk |
模拟为硬盘(C:) |
iso |
模拟为光驱(配合可引导 ISO) |
raw |
原始模式,不做几何结构转换 |
bigraw |
用于大镜像 |
c=N h=N s=N |
手工指定柱面/磁头/扇区数 |
进入 DOS 后的 Ghost 操作:
1. 启动 Ghost(ghost.exe)
2. Local → Partition → From Image
3. 选择镜像文件:找到 win7.gho(在 1:5 之类的分区里,Ghost 用自己的编号)
4. 选择源分区(镜像中的分区)
5. 选择目标磁盘与目标分区(务必看清容量与卷标,选错会覆盖数据!)
6. 确认 Yes 开始还原
7. 还原完成后重启
还原后必须修复 Windows 引导(否则 Windows 起不来):
方式一:用 Windows 安装盘/PE 的"修复计算机"功能
方式二:在 PE 或 DOS 下执行
bootsect /nt60 C: /mbr 写入 Windows 7 的引导代码
bcdboot C:\Windows /s C: 重建 BCD 引导配置
方式三:用 NTBOOTautofix、BOOTICE 等第三方工具自动修复
注意:bootsect /nt60 C: /mbr 会覆盖 MBR 的引导代码,导致 GRUB 被冲掉、Linux 无法启动。修完 Windows 后需要按附录 C.6 的方法重装 GRUB。
D.4 三种方式的对比与选择
| 方式 | 适用系统 | 关键参数/工具 | 难点 |
|---|---|---|---|
repo=hd: |
CentOS / RHEL / Fedora | repo= 或 inst.repo= |
需提取 isolinux/vmlinuz 与 initrd.img |
loopback + iso-scan |
Ubuntu / Mint / Debian | boot=casper iso-scan/filename= |
内核与 initrd 在 /casper/ 下,文件名版本间有差异 |
memdisk |
Windows / DOS / 各类工具盘 | linux16 /memdisk + initrd16 镜像 |
镜像整体载入内存,受内存大小限制;还原后需修 Windows 引导 |
通用排错要点:
file not found:GRUB 的分区编号写错(记住 grub2 分区从 1 开始),用grub> ls逐个确认;- 加载内核后卡住或找不到安装源:
repo=/iso-scan/filename=的路径写错,或分区设备名变了(改用UUID=或LABEL=); Kernel panic:内核与 initrd 版本不匹配(必须来自同一个 ISO);- 提取的
vmlinuz无法引导:BIOS 模式要用linux16/initrd16,UEFI 模式要用linuxefi/initrdefi; - 安装过程中不要格式化存放 ISO 的分区,否则安装源会中途消失导致失败;
- 装完 Linux 后,
/etc/grub.d/40_custom里的安装项应删除并重新grub2-mkconfig。
附录 E:命令速查总表
| 命令 | 用途说明 |
|---|---|
lsblk |
树形列出所有块设备、容量、类型与挂载点(-f 带文件系统与 UUID) |
fdisk -l |
列出所有磁盘的分区表;fdisk /dev/sdb 交互式分区(MBR,CentOS 7 也支持 GPT) |
parted |
交互/脚本式分区工具,支持 GPT 与 2TB 以上磁盘,操作即时写入 |
gdisk / sgdisk |
专用于 GPT 的分区工具,交互界面与 fdisk 类似 |
partprobe |
通知内核重新读取分区表 |
partx -a/-d/-u |
逐个添加/删除/更新分区到内核(比 partprobe 更细粒度) |
kpartx -av |
映射设备/镜像文件内的分区 |
sfdisk -d |
导出/导入分区表(备份分区结构的脚本化手段) |
mkfs / mkfs.ext4 / mkfs.xfs / mkfs.vfat / mkfs.ntfs |
创建文件系统(格式化) |
mke2fs |
ext 系列格式化的底层命令,选项最全 |
mount / umount |
挂载/卸载文件系统;mount -a 挂载 fstab 中所有条目 |
mount -o loop |
挂载 ISO 镜像或大文件模拟的磁盘 |
mount --bind |
把一个目录挂载到另一个位置(可用于探查被覆盖的挂载点) |
findmnt |
树形显示挂载关系;--verify 校验 fstab(CentOS 7) |
blkid |
查看各分区的 UUID、LABEL、TYPE |
df -h / df -i / df -T |
查看空间用量 / inode 用量 / 文件系统类型 |
du -sh / du -h --max-depth=1 |
统计目录占用;逐层定位大目录 |
lsof / fuser |
查找占用文件或挂载点的进程(解决 device is busy) |
lsof +L1 |
列出已删除但仍被占用的文件(df 与 du 不一致的元凶) |
e2label / xfs_admin -L |
设置 ext / xfs 的卷标 |
tune2fs |
调整 ext 文件系统参数(-l 查看、-m 预留比例、-U 改 UUID、-c/-i 自检周期、-L 卷标) |
dumpe2fs -h |
查看 ext 文件系统的超级块与详细信息 |
xfs_info |
查看 xfs 文件系统信息(参数是挂载点) |
xfs_admin -U/-L/-u/-l |
修改/查看 xfs 的 UUID 与卷标(需卸载) |
fsck / e2fsck -f -y |
检查修复文件系统(ext);必须在未挂载状态下操作 |
xfs_repair |
修复 xfs(-n 只检查,-L 清空日志、会丢数据) |
resize2fs |
调整 ext 文件系统大小(参数是设备名,可增可减) |
xfs_growfs |
扩大 xfs 文件系统(参数是挂载点,只能增大) |
xfs_fsr / e4defrag |
xfs / ext4 碎片整理 |
filefrag |
查看文件的碎片与 extent 分布 |
mkswap / swapon / swapoff |
创建、启用、停用交换空间;swapon -s 查看,swapon -p 设优先级 |
free -m |
查看内存与 swap 使用情况 |
dd |
底层块复制:备份 MBR、创建 swap 文件、整盘克隆、性能测试 |
head -c / truncate / fallocate |
创建指定大小的文件(用于 loop 设备或 swap 文件) |
losetup -a/-P/-d |
管理 loop 设备 |
pvcreate / pvs / pvdisplay / pvscan / pvremove |
物理卷(PV)的创建、查看、删除 |
pvmove |
在线把 PV 上的数据迁移到其它 PV(换盘不停机的关键命令) |
vgcreate -s / vgs / vgdisplay / vgscan / vgremove |
卷组(VG)管理,-s 指定 PE 大小 |
vgextend / vgreduce |
向 VG 添加/移除 PV;vgreduce --removemissing 处理丢失的 PV |
vgchange -ay/-an |
激活/停用 VG |
vgcfgbackup / vgcfgrestore |
备份/恢复 LVM 元数据(/etc/lvm/backup、/etc/lvm/archive) |
vgexport / vgimport |
卷组的导出与导入(迁移磁盘到另一台主机) |
lvcreate -L/-l/-n/-s/-i/-m |
创建逻辑卷、快照、条带卷、镜像卷 |
lvs / lvdisplay / lvscan / lvremove |
逻辑卷的查看与删除 |
lvextend -L/-l/-r |
扩大 LV(-r 同时扩文件系统) |
lvreduce -L/-r |
缩小 LV(危险,必须先缩文件系统) |
lvresize -r |
可增可减的统一命令(-r 自动处理文件系统) |
lvconvert --merge |
把快照合并回源卷(回滚) |
lvconvert -m1 |
把线性卷转为镜像卷 |
mdadm -C/-D/-E/-a/-r/-f/-S/-A/-G |
软 RAID 的创建、查看、添加、移除、标记故障、停止、组装、扩容 |
mdadm -Ds > /etc/mdadm.conf |
保存 RAID 配置(不做这步重启后阵列名会变) |
cat /proc/mdstat |
查看 RAID 状态与同步/重建进度 |
mdadm --zero-superblock |
清除成员盘上的 RAID 超级块(彻底删除阵列必需) |
wipefs -a |
擦除设备上的所有文件系统/RAID/分区表签名 |
cryptsetup luksFormat/luksOpen/luksClose/luksDump |
LUKS 磁盘加密的格式化、打开、关闭、查看 |
cryptsetup luksHeaderBackup |
备份 LUKS 头部(头部丢失等于数据永久丢失) |
quotacheck -cugv |
初始化 ext 的配额数据库 |
quotaon / quotaoff |
启用/停用配额 |
edquota / setquota |
交互式/非交互式设置配额;edquota -t 设宽限期,edquota -p 复制模板 |
quota / repquota -a |
查看用户配额 / 输出配额报表 |
warnquota |
给超限用户发送邮件告警 |
xfs_quota -x -c |
xfs 的配额管理(limit、report、state、timer、project) |
dump / restore |
ext 文件系统的分级备份与恢复(0~9 级) |
xfsdump / xfsrestore |
xfs 的分级备份与恢复(-l 级别、-L 会话标签、-f 目标、-I 查看清单) |
tar / rsync -avHAX |
通用的文件级备份(跨文件系统类型,最常用) |
badblocks -sv |
检测磁盘坏道(-n 非破坏性读写、-w 破坏性写测试) |
smartctl -H/-A/-a/-t |
读取 SMART 信息、判断磁盘健康、发起自检 |
hdparm -tT/-I |
磁盘读取性能测试与参数查询 |
iostat -x 2 |
磁盘 I/O 统计(关注 %util、await、avgqu-sz) |
iotop -o |
实时查看各进程的 I/O 占用 |
fio |
专业 I/O 压测工具(可指定读写模型、块大小、并发、队列深度) |
fstrim -av |
对 SSD / 精简置备存储执行 TRIM 回收 |
lsscsi / rescan-scsi-bus.sh |
列出 SCSI 设备 / 重新扫描 SCSI 总线 |
echo '- - -' > /sys/class/scsi_host/hostX/scan |
在线扫描新添加的磁盘 |
echo 1 > /sys/block/sdX/device/rescan |
刷新磁盘容量(虚拟磁盘扩容后使用) |
growpart |
在线扩大分区(cloud-utils-growpart 包) |
grub2-mkconfig -o / grub2-install |
重新生成 GRUB 配置 / 重装引导程序 |
grub2-set-default / grub2-editenv list / grub2-reboot |
设置默认启动项 / 查看 / 一次性指定下次启动项 |
dracut -f |
重建 initramfs(改了 RAID/LVM/加密配置后必做) |
efibootmgr -v |
管理 UEFI 固件中的启动项 |
附录 F:注意事项与最佳实践
-
分区、格式化、缩容都是高危操作,动手前必须确认设备名。
fdisk /dev/sdb与fdisk /dev/sda只差一个字母,后者可能直接摧毁系统盘。执行前用lsblk、fdisk -l、df -h三重确认,虚拟机上先做快照。 -
parted是即时写入的,没有 fdisk 的w确认环节。 一旦回车,mklabel、rm、mkpart立刻生效且不可撤销。生产环境用parted时格外小心,脚本化操作前先在测试机验证。 -
mkfs之前务必确认分区上没有数据。 格式化不会询问,也没有回收站。养成blkid、mount | grep、lsof三查的习惯。 -
改完
/etc/fstab必须立即执行mount -a(CentOS 7 再加findmnt --verify)验证。 报错就说明重启会出问题。改之前先cp /etc/fstab /etc/fstab.bak.$(date +%F)。非关键数据盘一律加nofail。 -
挂载点要用 UUID 或 LABEL,不要用
/dev/sdb1。 设备名会随磁盘发现顺序变化,可能导致挂载到错误的分区。LVM 设备名(/dev/mapper/vg-lv)是稳定的,可以直接用。 -
扩容与缩容的顺序是相反的,记错就丢数据:
- 扩容:先扩底层(
lvextend/resizepart),再扩文件系统(resize2fs/xfs_growfs); - 缩容:先缩文件系统(
resize2fs),再缩底层(lvreduce); - 用
-r选项(lvextend -r/lvreduce -r/lvresize -r)让工具自动按正确顺序处理,可有效避免人为失误。
- 扩容:先扩底层(
-
xfs 不能缩小,任何版本都不行。 CentOS 7 默认用 xfs,规划时就要考虑清楚:LV 初始只分配所需容量,靠后续在线扩容,从根本上避免缩容需求。确实需要频繁伸缩则选 ext4 或 LVM thin。
-
fsck、e2fsck、xfs_repair必须在文件系统未挂载时执行。 对已挂载(尤其是读写挂载)的文件系统执行修复,会造成更严重的损坏。根分区需要修复时,用救援模式或在 fstab 第 6 列设1/2让开机自动检查。 -
RAID 不是备份。 RAID 只能防硬盘物理故障,不能防误删除、误格式化、勒索病毒、逻辑错误、机房灾难。备份必须独立于生产存储,并且要定期做恢复演练——没验证过能恢复的备份等于没有备份。
-
创建 RAID 后必须
mdadm -Ds > /etc/mdadm.conf,否则重启后阵列可能被识别为/dev/md127,导致 fstab 挂载失败。同时启用mdmonitor并配置MAILADDR,否则盘坏了没人知道,等第二块盘也坏就来不及了。 -
LUKS 的口令与头部都必须妥善保管。 口令全部遗忘 = 数据永久丢失;头部(前 1~2MB)被覆盖 = 数据永久丢失。务必
luksHeaderBackup并异地保存备份文件(该文件含密钥材料,需chmod 400严格保护)。 -
dd是双刃剑,if与of写反就是灾难。 备份 MBR 是if=/dev/sda of=./mbr.bak,恢复是if=./mbr.bak of=/dev/sda。执行前把命令读三遍,尤其注意of=后面跟的是不是你想写入的目标。 -
不要直接
rm正在被写入的日志文件。 会导致空间不释放(lsof +L1可见)且服务丢失日志句柄。正确做法是> file清空,或交给logrotate管理。 -
/boot要留足空间并定期清理旧内核。/boot满会导致内核升级失败、dracut无法生成 initramfs。用package-cleanup --oldkernels --count=2清理,并在/etc/yum.conf设installonly_limit=2。 -
/var、/var/log、/home、/tmp生产环境建议单独分区或做成 LV。 根分区被撑满会导致系统性故障(无法登录、服务无法启动、sudo失效)。/bin、/sbin、/lib、/etc、/dev绝对不能单独分区。 -
配置磁盘空间与 inode 双重监控。
df -h正常但df -i满的情况极易被忽略,报错同样是No space left on device。 -
4K 对齐会显著影响性能。 用
parted的百分比方式或align-check optimal验证;现代fdisk/parted默认从 2048 扇区开始,一般已对齐,但从旧系统迁移或手工指定起始扇区时要复核。 -
虚拟机磁盘扩容需要刷三层:设备容量(
echo 1 > /sys/block/sdX/device/rescan)→ 分区(growpart/parted resizepart)→ 文件系统(xfs_growfs/resize2fs)。少一层就会出现"磁盘变大了但df没变"的困惑。 -
克隆虚拟机或整盘
dd后,必须处理 UUID 冲突。 两块 UUID 相同的盘会导致挂载结果不可预测。用xfs_admin -U generate/tune2fs -U random/pvchange -u/vgchange -u重新生成。 -
重要变更前后都要留痕:变更前备份配置文件(
fstab、mdadm.conf、lvm.conf)与分区表(sfdisk -d、sgdisk --backup、ddMBR),变更后记录lsblk -f、pvs/vgs/lvs、cat /proc/mdstat的输出,出问题时才有对照基准。
此处评论已关闭