为什么mkfs之后uuid没有立刻更新

为什么mkfs之后uuid没有立刻更新

Posted by lwk on July 1, 2020

问题背景:

虚拟机上通过mkfs.xfs格式化盘时,/dev/disk/by-uuid没有实时更新?

通过shell脚本对某块云盘进行格式化,并获取格式化后的/dev/disk/by-uuid/结果时,得到的结果时格式化前的旧数据。

脚本大概内容:

mkfs.xfs -f /dev/vdb

find /dev/disk/by-uuid/ -type l

但是通过在格式化和find之间加入sleep1就能得到最新结果。初步怀疑格式化更新/dev/disk/by-uuid不是实时的。

猜想:可能是子机执行时,陷入handle_exit到母机的qemu_io线程,最后由母机进行调度到cpu上执行耗时比较高导致?

验证1:

mkfs.xfs -f /dev/vde && stat/dev/disk/by-uuid/*

获取到by-uuid结果是旧的

验证2:

mkfs.xfs -f /dev/vde && sleep 1&& stat /dev/disk/by-uuid/*

获取到by-uuid结果是新的

验证3:

mkfs.xfs -f /dev/vde && sleep 0.5&& stat /dev/disk/by-uuid/*

获取到by-uuid结果是新的

验证4:

mkfs.xfs -f /dev/vde && sleep 0.02&& stat /dev/disk/by-uuid/*

获取到by-uuid结果偶尔是新的,偶尔是旧的

先自己通过GDB调试看下效果:

xfsprogs源码地址:

http://www.linuxfromscratch.org/blfs/view/svn/postlfs/xfsprogs.html

编译源码,并通过GDB单步调试。

1、先在虚拟机上执行GDB image image

image 核心调用函数关系如下:

image image image image image 从GDB调试看到,mkfs.xfs格式化盘使用的是fsync,理论上是同步方式刷新到磁盘上。但从实际结果和验证结果看却不是实时的。

2、同样,找了一台实体机,在上面进行测试发现也有相同的问题(/dev/disk/by-uuid不是实时更新的)。

那看下到底是谁更新/dev/disk/by-uuid

我们使用audit开启对/dev/disk/by-uuid的审计,看下到底是谁在什么时候write这个目录。

auditctl -w /dev/disk/by-uuid -p war

image

从审计日志可以看到是systemd-udevd对/dev/disk/by-uuid进行写操作。

udev是systemd的一部分,属于用户态的常驻进程。

image 经过对udev代码的分析,确实存在耗时问题。

mkfs.xfs /dev/** -> systemd-udev->event->/dev/disk/by-uuid image 查看一下UDev的存储设备规则,可以看到块设备文件的UUID是根据UDev的$env{ID_FS_UUID_ENC}环境变量而来

文件:/usr/lib/udev/rules.d/60-persistent-storage.rules

image 虚拟机上测试从格式化盘到更新uuid大概耗时20ms image

实体机上测试从格式化盘到更新uuid大概耗时10ms image

总结:mkfs对盘进行格式化时(重做文件系统),不是实时生成的uuid,即mkfs完成之后,/dev/disk/by-uuid不会立刻生成新的uuid,对于vm大概需要20ms左右才能更新uuid,对于实体机大概需要10ms左右。之所以实体机测试相比虚拟机上耗时小,可能是因为虚拟化原因,虚拟机上执行udev实际是通过qemu_io线程执行的,即:

handle_exit->kvm_handle_io->kvm_vcpu_ioctl->kvm_run,通过母机上的qemu线程再调度到母机的cpu执行具体结果再返回给虚拟机过程耗时导致的。

因此在对磁盘进行格式化之后,若获取最新的uuid,可以先sleep 1s 之后再获取。