不存在
你有没有遇到过这种情况:给一台运行中的Linux服务器扩了磁盘容量,或者用fdisk新划了一个分区,敲完w保存退出,结果系统却告诉你“内核还在使用旧的分区表”,或者fdisk -l能看到新分区,但/dev/sdb1这个设备节点就是不存在。
传统的做法是重启机器让内核重新读取分区表。但在生产环境里,重启一台数据库服务器、一台K8s节点,代价往往很大。而且很多时候你只是加了一块盘、扩了一个分区,根本没必要让整个系统停机。
这个问题其实有非常成熟的解法,核心思路就是调用内核的BLKRRPART(重新读取分区表)机制,或者直接通过sysfs、设备映射工具来刷新分区信息。本文我会结合真实场景,把“不重启Linux就能让新分区表生效”这件事讲透,覆盖工具对比、完整实操步骤、常见坑和排查方法。
1. 为什么内核会“看不见”新的分区表
1.1 内核维护的“分区表缓存”机制
很多人以为修改了磁盘上的分区表,内核马上就能感知到。实际上不是这样。Linux内核在识别一块磁盘时,会通过add_disk()把分区信息加载到内存里,形成gendisk结构体和一系列block_device对象。也就是说,内核在磁盘插入、系统启动时已经“快照”了一份分区信息,后续你在用户态用fdisk修改分区表,这块快照不会自动更新。
这就好比你去餐厅点餐,菜单(内核里的分区信息)是进门时拿到的,后厨(磁盘)改了菜单,但你手里的那份还是旧的。你得主动找服务员换一份,对应到Linux里,就是主动通知内核“磁盘分区表变了,请重新读一遍”。
1.2 触发重新读取的底层机制
Linux提供了一套ioctl接口来操作块设备,其中最关键的是BLKRRPART。调用这个ioctl时,内核会清除该磁盘已有的分区缓存,然后重新读取磁盘上的分区表,并注册新的分区设备节点。几乎所有的“重读分区表”工具,最终都是围绕这个机制来做文章的。
但这里有个细节:如果磁盘上某个分区正在被使用(比如被mount、被LVM占用、被某进程以文件句柄打开),内核在删除旧分区缓存时会失败,返回EBUSY(Device or resource busy)。这就解释了为什么在某些场景下,你敲完命令会看到Failed to update partition table之类的报错。
理解了这一点,后面的所有工具和排错思路就都顺理成章了。
1.3 哪些场景真正需要“不重启重读分区表”
- 在运行中的服务器上新增了一块磁盘,分区后想立即格式化并使用。
- 虚拟机磁盘扩容(比如VMware、KVM、OpenStack里给云主机加容量),需要让SCSI层扫描到新大小,再重读分区表。
- 直接修改磁盘原有分区表(删除分区、新建分区、改分区类型),但不想重启。
- 云盘或物理阵列扩容后,分区表尺寸变化,需要扩展分区和文件系统。
这类操作在运维日常里非常高频,属于标准“生存技能”。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 四条常用路径,选哪个工具更合适
2.1 工具矩阵对比
Linux下能实现“不重启重读分区表”的方式并不只有一种,常用的有partprobe、hdparm -z、blockdev --rereadpt、partx,以及通过sysfs触发SCSI设备重扫。它们各有侧重,先看一个总览:
| 工具 | 核心原理 | 适用场景 | 注意事项 |
|---|---|---|---|
partprobe |
调用BLKRRPART,或兼容层刷新分区 | 通用场景,parted工具集自带 | 磁盘整体被占用时可能失败 |
hdparm -z |
对指定磁盘触发BLKRRPART | 简单快速,常用于SATA/SCSI盘 | 对设备整体操作,占用时失败 |
blockdev --rereadpt |
同样是BLKRRPART | 脚本化操作友好,输出简单 | 跟hdparm类似,整体刷新 |
partx -u |
直接调用add/delete partition接口,增量更新 | 服务器已有分区被使用、只想加一个新分区时 | 不依赖BLKRRPART,灵活度最高 |
| sysfs SCSI重扫 | 触发SCSI驱动重新扫描通道或设备 | 虚拟机磁盘扩容、新挂盘未识别时 | 针对SCSI设备,不能直接刷新分区表 |
2.2 partprobe:最通用的“首选方案”
partprobe是parted包里的一个命令,几乎所有主流的Linux发行版默认都会装上。它的逻辑是先尝试BLKRRPART,如果因为分区被占用而失败,会通过内核的设备模型触发一次udev事件,让分区设备节点重新生成。
我的习惯是:新分区表应用后,第一选择永远是partprobe /dev/sdX,因为它兼容性最好。唯一的问题是某些老版本内核上,如果磁盘在忙,它也可能无疾而终,这时就得配合partx来做增量刷新。
2.3 hdparm与blockdev:更适合脚本化环境
hdparm -z /dev/sdX是老牌工具,对IDE和SCSI/SATA设备都有效,底层也是触发BLKRRPART。它的好处是命令短、参数少,适合写到自动化脚本里。
blockdev --rereadpt是util-linux包里的命令,特点就两个字:干净。它会直接丢掉内存里的分区表并重新读取,成功与否有明确返回码,适合做脚本判断。
2.4 partx:对“个别分区被占用”场景的救命稻草
partx最大的优势是不走BLKRRPART整盘刷新路线,而是通过内核的add_partition和delete_partition接口,直接增加或删除某一个分区设备节点。这意味着即使其他分区处于繁忙状态,你仍然可以单独让某个新分区生效。
我自己遇到最多的情况是:服务器上跑着一个MySQL实例,数据目录挂在/dev/sdb1上,这时候我想在同一块盘上再新建分区/dev/sdb2并格式化。直接partprobe /dev/sdb大概率报busy,但partx -u /dev/sdb可以嬉皮笑脸地只把sdb2加进来,不影响sdb1正在跑的业务。
2.5 sysfs重扫:虚拟机扩容的真正入口
还有一个容易忽略的场景:在虚拟化平台上给虚拟机磁盘扩容后,虚拟机里的系统压根不知道磁盘变大了。这时光刷新分区表没用,得多走一步——让SCSI层重新扫描设备。
bash复制# 重新扫描所有SCSI主机通道,适用于新增磁盘或磁盘扩容后的容量感知
for host in /sys/class/scsi_host/host*; do
echo "- - -" > "$host/scan"
done
# 对单块磁盘做rescan,适合容量变化的情况
echo 1 > /sys/block/sdb/device/rescan
这条路径理解清楚后,很多“扩容后fdisk看到的容量没变”的困惑就能解开了。
3. 三个典型实操场景,从新盘到扩容一次讲透
3.1 场景一:新增一块磁盘并完成分区应用
假设服务器新挂了一块20GB的虚拟盘,系统识别为/dev/sdb,我需要把它分成两个分区:一个数据盘,一个备份盘。
bash复制# 1. 查看系统是否识别到新盘
lsblk
# 输出里能看到sdb,大小20G,没有分区
# 2. 用fdisk分区
fdisk /dev/sdb
# 按n创建两个主分区,按w保存退出
保存退出后,fdisk -l /dev/sdb能看到分区信息,但ls /dev/sdb*可能什么都没有,或者只有旧的节点。这时执行:
bash复制# 3. 重读分区表
partprobe /dev/sdb
# 4. 确认新的分区设备节点已经出现
lsblk /dev/sdb
正常情况下,/dev/sdb1和/dev/sdb2会出现,可以继续格式化和挂载。
如果partprobe报错Error: Partition(s) on /dev/sdb are being used,说明这块盘上已经有被占用的分区,改用增量方式:
bash复制partx -u /dev/sdb
这个命令会只添加缺失的分区节点,已经在用的分区不受影响,是最适合“新盘上有老业务”场景的办法。
3.2 场景二:虚拟机磁盘在线扩容后更新容量感知
我在VMware里给一台CentOS 7虚拟机的系统盘从50GB扩到100GB,重启前系统还认为磁盘是50GB。操作路径是:
bash复制# 1. 让SCSI层感知到磁盘新容量
echo 1 > /sys/class/scsi_device/2:0:0:0/device/rescan
如果你不确定SCSI设备设备的编号,直接用循环扫一遍所有SCSI主机通道也可以:
bash复制for host in /sys/class/scsi_host/host*; do
echo "- - -" > "$host/scan"
done
做完这一步,再执行lsblk,应该能看到磁盘容量已经变为100GB,但分区大小还是旧的。
接下来需要扩展分区。在线调整分区表,推荐用growpart,避免fdisk手动删除重建分区的风险:
bash复制# 2. 将第一个分区扩展到磁盘末尾(CentOS默认系统盘通常分区1或分区2为根分区)
growpart /dev/sda 1
扩展完成后,重读分区表:
bash复制partprobe /dev/sda
最后扩展文件系统。这里有个关键坑:xfs文件系统只能扩大不能缩小,而且必须挂载状态下用xfs_growfs;ext4/3用resize2fs。
bash复制# 如果根分区是xfs(比如CentOS 7/8/RHEL)
xfs_growfs /
# 如果是ext4
resize2fs /dev/sda1
3.3 场景三:修改已有分区表后的完整应用流程
再举一个更贴近日常的例子:我在/dev/sdc上原本有一个分区/dev/sdc1,现在想把它删掉,重新建一个更大的分区,但磁盘上还有/dev/sdc2正挂在业务目录下。
bash复制fdisk /dev/sdc
# 删除分区1,重新创建分区1,w保存
注意:这种修改带有一定风险,删除重建分区时不要动分区2的起始扇区。保存后,如果直接partprobe,极可能报错,因为分区2在用。这时候增量刷新是最稳的:
bash复制# 删除旧分区节点(如果还存在)
partx -d /dev/sdc1
# 重新读取新分区表里的sdc1信息
partx -a /dev/sdc
partx -d会移除指定分区的设备节点,partx -a会把内核里缺失的分区设备节点都补上。这个组合比整盘刷新要温和很多。
4. 常见报错与排错手册
4.1 “Device or resource busy”是怎么回事
这是最经典的报错。“busy”的本质是内核在清理旧分区缓存时,发现该分区还处于被使用状态。排查思路:
bash复制# 查看分区挂载情况
mount | grep sdX
# 查看是否有进程占用该分区上的文件
fuser -vm /data
lsof /data
如果确认不再需要挂载,先卸载,再重读分区表:
bash复制umount /dev/sdX1
partprobe /dev/sdX
如果进程原因暂时无法释放,又必须让新分区表生效,直接用partx -a加入新分区,别动正在用的部分。
4.2 重读成功了但看不到新设备节点
遇到partprobe执行成功,但/dev/sdb3迟迟不出现的情况,可以分两步排查:
第一步,检查分区信息是否真的写进了内核:
bash复制cat /proc/partitions
看不到说明内核还没识别。第二步,手动触发设备节点创建:
bash复制# 让udev按当前分区表重新生成设备节点
partx -u /dev/sdb
# 或者让udev主动重扫一下
udevadm settle
多数情况下,partx -u一敲就出来了。
4.3 扩容后LVM没感知到新空间
这一条在扩容场景特别常见。分区扩大了,对应的物理卷大小却还停在旧值。检查:
bash复制# 查看物理卷当前状态
pvdisplay /dev/sdb1
如果PV Size还是旧容量,执行:
bash复制pvresize /dev/sdb1
然后卷组和逻辑卷也会自动扩展空间。需要注意:pvresize之前必须确保分区表重读成功,否则它看不到物理分区的新尺寸。
4.4 根分区始终无法在线扩容
这应该是很多人的最终痛点。根分区在安装系统时占了整块盘,扩容时又不敢乱动。好消息是,现代Linux对根分区在线扩容的支持已经很成熟,前提是按顺序执行:
bash复制# 1. 扩分区
growpart /dev/sda 1
# 2. 刷新分区表
partprobe /dev/sda
# 3. 扩文件系统(xfs为例)
xfs_growfs /
这里最关键的坑是分区设备号:如果根分区在/dev/sda上,且是第一个分区,那就是growpart /dev/sda 1;如果系统盘是NVMe盘,设备名是/dev/nvme0n1,分区号是p1,命令变成growpart /dev/nvme0n1 1。搞错了设备名会直接报错,但不会损坏数据,放心试。
4.5 分区表类型(MBR/GPT)带来的差异
老式MBR分区表只有4个主分区,对分区数量、大小上限(单分区2TB之内)有限制。GPT分区表则没有这些问题。当你用fdisk操作一块大于2TB的盘时,建议直接新建GPT分区表。
bash复制parted /dev/sde
mklabel gpt
Parted创建的分区默认会执行partprobe动作,所以新分区表通常会自动生效。但在脚本场景里我依然习惯手动补一次partprobe,确保万无一失。
4.6 一台机器上有多块盘,怎么批量刷新
如果一次性挂了很多盘(比如在一个新部署的存储节点上加了8块盘),可以写个循环批量刷新:
bash复制for disk in /dev/sd{b,c,d,e,f,g,h,i}; do
partprobe "$disk"
done
也可以用lsblk -d -o NAME | tail -n +2动态获取没有分区的磁盘列表,再根据策略分区和刷新,进一步解放双手。
5. 几条硬核实操经验与避坑底线
5.1 动手前永远先备份分区表
这是最容易被忽略的一步。虽然重读分区表不会直接改磁盘数据,但很多操作会紧跟在“重读”之后执行,比如恢复分区、重建文件系统。一旦分区表本身损坏,轻则数据暂时不可见,重则需要花费大量时间恢复。
bash复制# 备份MBR分区表
sgdisk --backup=/root/sda_partition_table.backup /dev/sda
# 如果是GPT
gdisk /dev/sda
# 然后在交互界面使用x,再选择w,保存备份
或者直接用sfdisk:
bash复制sfdisk -d /dev/sda > /root/sda_partition_table.dump
恢复时只要sfdisk /dev/sda < /root/sda_partition_table.dump就能还原分区结构。
5.2 有条件就优先用growpart替代fdisk调整分区边界
手动用fdisk删除分区再重建,一旦起始扇区填错,整个分区结构就乱了。而growpart这种自动化工具会在原分区起始位置不变的情况下,把结束位置推到磁盘末尾,安全性高很多。这也符合“最小改动”原则。
bash复制# 把某个磁盘的第三个分区扩展到最大
growpart /dev/sdf 3
命令执行完还是那句话,partprobe刷新一下再扩展文件系统。
5.3 “重读分区表”不等于“更新文件系统”
很多新人踩的一个重复大坑是:扩容分区后,以为partprobe完事大吉,结果df -h一看,空间没变。原因很简单——分区表和文件系统是两个层面。分区表扩容后,文件系统还保持原始大小,必须用resize2fs(ext系列)或xfs_growfs(xfs)去扩展文件系统,空间才会真正落到可用容量里。
如果是ext4文件系统,且分区已经挂载,可以这样:
bash复制resize2fs /dev/sdj1
5.4 不要轻易在“根分区设备”上尝试整盘刷新
根分区所在设备一般处于相当繁忙的状态。此时对整块盘执行partprobe /dev/sda,有一定概率导致根分区设备节点短暂消失,虽然一般能自动恢复,但生产环境里这种抖动风险不值得冒。
如果只是要在根盘上增加一个新分区(比如把磁盘剩余空间切成一个新分区用于日志),我强烈建议只走partx -a /dev/sda,不要动整盘刷新。这也是我踩过坑之后养成的习惯。
6. 我的最终实战套路
根据个人经验,我把“Linux磁盘分区/扩容”的完整处理顺序归纳成一套固定流程,分享出来可以直接参考:
bash复制# 第一步:确认磁盘状态
lsblk
# 第二步:如果需要系统感知新容量,先重扫SCSI
echo "- - -" > /sys/class/scsi_host/host0/scan
# 或者单盘rescan
echo 1 > /sys/block/sdX/device/rescan
# 第三步:分区(fdisk/parted/growpart)
growpart /dev/sdX 1
# 第四步:重读分区表,优先增量方案
partx -u /dev/sdX
# 如果不涉及在用的分区,直接用partprobe /dev/sdX
# 第五步:扩展文件系统前先确认文件系统类型
blkid /dev/sdX1
# ext4
resize2fs /dev/sdX1
# xfs(挂载状态下执行)
xfs_growfs /挂载点
# 第六步:确认结果
df -hT
这套流程我在物理机、虚拟机、云主机上都跑过,核心逻辑就是:感知容量 -> 修改分区 -> 刷新分区 -> 扩展文件系统。四步一条龙,基本覆盖90%以上的在线扩容需求。
最后分享一个容易被忽视的小技巧:如果你在脚本里执行partprobe后立即执行mkfs或mount,偶尔会遇到“设备节点还没准备好”的竞态问题。稳妥的做法是在刷新命令后面加上udevadm settle,等设备节点稳定后再继续。这个细节让我节省了大量排查时间,希望也能帮到你。
