在一台虚拟机上给硬盘分了新分区、建好了文件系统,一切正常。切换回宿主机,想直接看看那块盘的分区情况——结果宿主机的工具里,新分区根本没出现,显示的还是老样子。你以为虚拟机里的操作没生效,回去一看,虚拟机上它好好的。两边看到的世界不一样。
现象
具体表现:
- 在虚拟机内部对(直通的)块设备新建了分区;
- 宿主机的分区查看工具里,看不到这个新分区;
- 宿主机显示的仍是旧的分区布局(或者只有老分区);
- 重启宿主机之后,新分区出现了——但你不能随便重启一台生产机。
如果你在宿主机上做取证、或者想直接在宿主上挂载那块盘,这个"看不到"会直接挡住后续操作。
根因
根本原因是:宿主机内核持有的分区表缓存是旧的,不会自动重读。
这里要理解两件事。
第一,内核在内存里缓存了分区表。系统启动、或设备插入时,内核读一次磁盘的分区表,把"这块盘有几个分区、从哪到哪"记在内存里,之后各工具看到的都是这份缓存。这是为了性能——不能每次读分区信息都去敲一次磁盘。
第二,内核只在特定事件发生时才会重读分区表。比如设备接入、partprobe 之类的命令触发、或者重启。当别人(这里是虚拟机)绕过宿主机内核、直接往块设备写入了新分区表时,宿主机的内核不知道这件事发生了——没有事件通知它,于是它继续用内存里那份旧缓存。结果就是:盘上的分区表其实已经变了,但宿主机内核还认为它是老样子,工具读缓存,自然看不到新分区。
这个场景之所以常见,是因为虚拟化下"直写块设备"很普遍:虚拟机被允许直接操作整块物理盘(或分区)时,写操作不经过宿主机内核的文件系统层,宿主机就没有机会同步自己的分区表缓存。
一句话:宿主内核的缓存和磁盘上的真实分区表脱节了。工具报的是缓存,不是盘。
解决
方向是绕开宿主内核的缓存,直接去读盘上的真实分区表。有几种做法:
一、用直接读分区表的工具查看。 这类工具自己解析磁盘头部的分区表结构,不依赖内核缓存:
# sfdisk 打印分区表(-d 导出为可重建的格式)
sudo sfdisk -d /dev/sdX
# 或用 partx 列出分区表里的条目
sudo partx -l /dev/sdX
如果目的是取证(看盘上到底写了什么),这些工具读到的才是盘的真实内容。
二、把整盘只读挂成 loop 设备再看。 让系统在整块盘上建立一个新的 block device 视图,重新解析分区:
# 找空闲 loop 设备,只读方式关联整盘
sudo losetup -r -f --show /dev/sdX
# 若要连同分区表一起解析出子设备
sudo losetup -r -P -f --show /dev/sdX
# 之后 /dev/loopNp1 之类就是按盘上分区表解析出来的分区
-r 保证只读,避免误写;-P 让它解析分区表、生成对应的分区设备。这样看到的是盘上实际的分区布局。
三、如果确实要让宿主内核更新缓存,可以触发重读(有风险,需谨慎):
# 让内核重读指定设备的分区表
sudo partprobe /dev/sdX
# 或
sudo blockdev --rereadpt /dev/sdX
但要注意:如果该设备上已有分区正被挂载或使用,重读可能因为"设备忙"而失败,或在极端情况下造成数据风险。在挂载了该盘的生产环境里,别随手执行。更稳妥的是走前两条(只读查看)的路子,只在确认无挂载、可控的前提下才动缓存。
延伸与预防
这条坑的通用价值,是**"内核缓存会骗你,取证时要绕开它"**。
内核为了性能到处做缓存——分区表、文件属性、目录项、页缓存。正常情况下这些缓存会随着内核自己的操作保持同步,所以你不会察觉。但当有人绕过内核、从旁路直接改动底层数据时,缓存就成了"过期的真相",报出来的是旧世界。
这个模式在别处同样成立:
- 取证场景下读磁盘,要尽量只读、从底层读,别用可能被缓存/被日志重塑的视图;
- 直接改了裸设备或 LVM 元数据后,要在相关层触发同步或刷新,否则上层看到的还是旧布局;
- 在存储栈的多层之间(宿主 / 虚拟机 / 容器 / 设备)操作同一份数据,每一层都可能有自己的缓存视图。
预防上两条:在虚拟化环境里直写块设备后,任何依赖内核缓存的查看都先存疑;以及取证优先用只读、绕缓存的方式(sfdisk -d、只读 loop),把"看到的准不准"这件事先解决掉,再谈后面的分析。养成"这是谁在读、走没走缓存"的自觉,能避免很多"明明改了却没生效"的困惑。