手机站:/m

大数据共享平台-使用DTrace调试卸载问题

时间:2021-01-09 16:46编辑:淘客樊里来源:淘客樊里当前位置:主页 > 云存储 >

分析几周前,我在产品的单元测试过程中遇到了一个再次出现的问题,错误消息的重要部分总是这样的:cannot unmount'/test domain/group-0/container-22/temp':Device busy无法销毁"test domain":无法卸载数据集一点调查显示错误消息来自单元测试框架脚本中的"zpool destroy"命令。"zpool destroy"命令在释放相关数据之前卸载池中的所有文件系统。在这种情况下,卸载失败了。这大概是因为一些流氓进程正在访问文件系统,这是令人惊讶的,因为测试是在精简的虚拟机上运行的,没有其他用户,也没有进程与单元测试创建的文件系统有任何业务往来。再多调查一下,结果令人失望:该误差不可靠地再现。它发生的频率足够让人恼火,因为它会导致一个干净的单元测试运行失败,但很少发生,以至于您需要在执行测试之前反复运行几个小时。在任何特定的测试之后或在任何特定的文件系统上都没有发生错误。有些测试似乎更容易失败,但几乎所有测试至少失败一次。测试失败后,手动运行相同的zpool destroy命令将成功,因此尝试识别访问文件系统的进程已经太晚了。考虑到最后一点,逻辑上的下一步就是找出卸载发生在内核中的什么位置,并在失败时强制执行崩溃转储。然后我们可以检查崩溃转储,看看哪个进程正在访问该文件系统。将源代码浏览到我们的基于Illumos的操作系统时,在zfs_vfsops.c中生成了函数zfs_umount(vfs_t*vfsp,int fflag,cred逯t*cr),这是一个很可能导致卸载失败的地方,而这个DTrace one liner导致它在失败时崩溃转储:DTrace-wn"fbt::zfs"_umount:返回/arg1==EBUSY/{panic();}"对于那些不熟悉DTrace的人来说,下面是故障:探测器fbt::zfs_umount:还击当zfs_umount()返回时。谓词/arg1==EBUSY/表示只应在返回的值(arg1)为EBUSY时执行列出的操作。panic()操作使内核恐慌。必须使用-w选项将DTrace设置为"破坏性模式",以便运行panic()操作。崩溃转储显示传入zfs_umount()的vfs_t的vfs_count字段大于1,这是导致卸载失败的原因。不幸的是,vfs_count是一个简单的整数引用计数,由对vfs_hold(vfs_t*vfsp)和vfs_rele(vfs_t*vfsp)的调用管理,没有指向持有者的反向指针(我希望找到类似ZFS的refcount_t)。我把这些发现告诉了同事亚当·列文塔尔,他指出,必须在卸载失败后立即释放货舱,因为后来试图摧毁鱼塘的尝试成功了。这个观察结果导致了这个DTrace脚本,它输出进程的堆栈跟踪,这些进程在未能卸载的vfs上释放它们的保留:/*卸载d*//**我们将存储一个指向未能在中卸载的vfs\t的指针*"traceme"变量,它以NULL开头。*//**当输入zfs_umount()时,请记住vfs_t参数以防*卸载失败。*/fbt::zfs_umount:输入{自我->zfsvfs=arg0;}/**如果zfs_unmount()失败,则将失败的vfs_t参数存储到*"traceme"变量。 */fbt::zfs_umount:返回/arg1==EBUSY/{traceme=自我->zfsvfs;}/**当对先前相同的vfs_t调用vfs_rele()时*无法卸载打印大量详细调试*信息。*/fbt::vfs_rele:进入/arg0==traceme/{printf("%s[%d,%d]:%s",execname,pid,ppid,curpsinfo—>;pr_psargs);堆栈();ustack();}经过几次单元测试后,罪魁祸首宣布:$dtrace-s umount.d美元CPU ID功能:名称0 21673心室颤动_rele:进入vmtoolsd[882,1]:/usr/lib/vmware tools/sbin/amd64/vmtoolsdzfs`zfs_znode_free+0x80zfs`zfs_zinactive+0x9bzfs`zfs_非活动+0x11cgenunix`fop_不活动+0xafgenunix`vnüu rele+0x5fgenunix`lookuppnvp+0x99agenunix`lookuppnatcred+0x11bgenunix`lookupnameatcred+0x97genunix`lookupnameat+0x69genunix`cstat getvp+0x12bgenunix`cstatat+0x5cgenunix`fstatat+0x4cgenunix`stat+0x25unix`sys\u syscall+0x17alibc.so公司.1`\u syscall6+0x1blibvmtools.so`Posix_Stat+0x9flibvmtools.so`0xfffffd7ffda5b5b3libvmtools.so`刮水器分区打开+0x76libvmtools.so`留言信息+0x43libguestInfo.所以`0xfffffd7ffd202acclibglib-2.0.so.0`0xfffffd7ffa3ac4dlibglib-2.0.so.0`0xfffffd7ffaa380d3libglib-2.0.so.0`g_main_context_dispatch+0x6dlibglib-2.0.so.0`0xfffffd7ffaa397belibglib-2.0.so.0`g_main_loop_run+0x2b0vmtoolsd`0x403140vmtoolsd`0x402c1dvmtoolsd`0x40288c0 21673心室颤动_rele:进入vmtoolsd[882,1]:/usr/lib/vmware tools/sbin/amd64/vmtoolsdzfs`zfs_znode_free+0x80zfs`zfs_zinactive+0x9bzfs`zfs_非活动+0x11cgenunix`fop_不活动+0xafgenunix`vnüu rele+0x5fgenunix`cstatat+0x8agenunix`fstatat+0x4cgenunix`stat+0x25unix`sys\u syscall+0x17alibc.so公司.1`\u syscall6+0x1blibvmtools.so`Posix_Stat+0x9flibvmtools.so`0xfffffd7ffda5b5b3libvmtools.so`刮水器分区打开+0x76libvmtools.so`留言信息+0x43libguestInfo.所以`0xfffffd7ffd202acclibglib-2.0.so.0`0xfffffd7ffa3ac4dlibglib-2.0.so.0`0xfffffd7ffaa380d3libglib-2.0.so.0`g_main_context_dispatch+0x6dlibglib-2.0.so.0`0xfffffd7ffaa397belibglib-2.0.so.0`g_main_loop_run+0x2b0vmtoolsd`0x403140vmtoolsd`0x402c1dvmtoolsd`0x40288c结果是,vmwaretools安装了一个init脚本,该脚本启动了vmtoolsd二进制文件,该二进制文件似乎在调用stat(2),从而在要卸载的文件系统上放置了一个暂挂。虽然这些工具是以二进制形式分发的,但是可以从VMware的openvmtools项目中获得一些源代码的变体。幸运的是,DTrace输出已经在工具的代码库中为我们指明了正确的方向:在GuestInfo_GetDiskInfo()或wipperPartition_Open()函数周围。读取代码后发现vmtoolsd每隔几秒钟就从客户操作系统收集系统信息。作为数据收集的一部分,它从/etc/mnttab获取已装载文件系统的列表,并在每个装载点上调用stat(2)。这解释了我们在单元测试中看到的行为:任何在清理过程中卸载文件系统的测试都有机会与vmtoolsd的stat(2)调用竞争。我将在这里暂停一下,说DTrace刚刚帮助我们做了一些非常巧妙的事情。通过大约10行代码,我们不仅能够判断出是哪个进程导致了我们的问题,而且还可以判断出该进程二进制文件中的哪些函数负责。该脚本可以安全地在生产环境中运行,并且应该用于查找干扰卸载操作的其他恶意进程的实例(尽管编写时它是ZFS特有的)。最近,Delphix的另一位同事mattahrens用它发现了一个类似的NFS锁守护进程的竞争。"解决方案"我们无法在不丢失VMware tools的其他功能的情况下卸载或禁用vmtoolsd,但通过对VMware tools源代码的进一步检查发现:stat(2)中的信息仅用于确定文件系统是否安装在"磁盘设备"上,这是确定VMware是否支持该文件系统的某些特殊功能的一部分,但由于不支持ZFS,因此稍后检查将失败。由于ifdef,stat(2)操作的结果实际上在基于Solaris的操作系统上被忽略。因为我们确信这些stat(2)调用在我们的系统上是不必要的,亚当建议我们试着欺骗vmtoolsd,让他们认为没有挂载的文件系统,所以我们创建了这个讨厌的东西:/*颠覆vmtoolsd.d*/#pragma D选项破坏性系统调用:打开:入口/execname=="vmtoolsd"&;copyinstr(arg0)=="/etc/mnttab"/{此->;f="/dev/null";复制(this->;f,arg0,strlen(this->;f)+1);}与umount.d脚本不同,这个脚本绝对不安全。此脚本拦截vmtoolsd发出的所有打开(2)调用。如果正在打开的文件是"/etc/mnttab",DTrace将用"/dev/null"覆盖vmtoolsd内存中的文件名,因此它最终打开的是一个空文件。请注意,/dev/null比/etc/mnttab少的字符在这里很重要,如果它更长,它可能无法放入vmtoolsd分配给open(2)的缓冲区。显然,这是一个次优的解决方案,但目前看来它是有效的。为了确认,我们检查了:dtrace-n"syscall::stat:条目/执行名称=="vmtoolsd"/{print("%s",copyinstr(arg0);}",它不再显示我们的文件系统装载点。看起来openvm工具的最新开发快照没有这个问题,所以这将使我们的单元测试保持愉快,直到这些更改被引入到官方的VMware工具中,或者我们自己开始构建开发快照。

上一篇搭建大数据平台-奥罗拉Juniper村的汤姆亨特

下一篇大淘客推广-Veeam准备好支持云层了!

养花知识本月排行

养花知识精选