问题背景:
虚拟机上通过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
核心调用函数关系如下:
从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
从审计日志可以看到是systemd-udevd对/dev/disk/by-uuid进行写操作。
udev是systemd的一部分,属于用户态的常驻进程。
经过对udev代码的分析,确实存在耗时问题。
mkfs.xfs /dev/** -> systemd-udev->event->/dev/disk/by-uuid 查看一下UDev的存储设备规则,可以看到块设备文件的UUID是根据UDev的$env{ID_FS_UUID_ENC}环境变量而来
文件:/usr/lib/udev/rules.d/60-persistent-storage.rules
虚拟机上测试从格式化盘到更新uuid大概耗时20ms
实体机上测试从格式化盘到更新uuid大概耗时10ms
总结: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 之后再获取。