dlclose并不真正卸载共享对象,无论它被调用了多less次

我的程序使用dlopen来加载一个共享对象,然后dlclose卸载它。 有时这个共享对象再次加载。 我注意到静态variables没有重新初始化(这对我的程序是至关重要的),所以我在dlclose后添加了一个testing(使用RTLD_NOLOAD dlopen )来查看库是否真的被卸载。 果然,这仍然是在记忆中。

然后我试着重复调用dlclose直到这个库真的被卸载,但是我得到的是一个无限循环。 这是我用来检查库是否卸载的代码:

 dlclose(handles[name]); do { void *handle = dlopen(filenames[name], RTLD_NOW | RTLD_NOLOAD); if (!handle) break; dlclose(handle); } while (true); 

我的问题是, dlclose后,我的共享对象没有被卸载的可能原因是什么,因为我的dlopen调用是唯一加载的地方。 你能提出一个行动路线来追踪问题的根源吗? 另外,为什么重复调用dlclose没有任何作用,它们都是递减引用计数,不是吗?

编辑:刚刚发现这只发生在我用gcc编译时。 随着叮当 ,一切都很好。

POSIX标准实际上并不要求dlclose从地址空间卸载一个库:

虽然不需要使用dlclose()操作从地址空间中 删除结构 ,但是也不允许这样做。

来源: Open Group基本规格问题6

这意味着除了使句柄无效之外, dlclose并不需要做任何事情。

有时卸载也被系统延迟,它只是标记库被“删除”,并且会在稍后的时间(为了效率或者因为现在根本无法执行该操作)而实际执行该操作。 但是,如果在执行之前再次调用dlopen ,则会清除该标志,并重新使用仍然加载的库。

在某些情况下,系统肯定知道库的某些符号仍在使用中,在这种情况下,它不会从地址空间中卸载它,以避免悬挂指针。 在某些情况下,系统并不确定它们正在被使用,但它也不可能确切地告诉他们,它们不是最好的安全而不是对不起,在这种情况下,永远不会真的从内存中删除这个库。

还有其他更隐晦的情况,取决于操作系统类型,通常也在版本上。 例如,一个常见的Linux问题是,如果您创建了一个使用STB_GNU_UNIQUE符号的库,该库被标记为“不可卸载”,因此将永远不会被卸载。 在这里 看到 ( DF_1_NODELETE表示不可卸载), 在这里 。 所以它也可以取决于编译器生成什么样的符号或符号。 尝试在库上运行readelf -Ws并查找标记为UNIQUE对象。

一般来说,你不能真正依靠dlclose来工作。 在实践中,我认为在过去的十年中,这个“失败”比“成功”更多(呃,从来没有真正失败过,它往往没有从内存中卸载图书馆,但是它按标准要求工作)。

动态库加载有很多怪癖。 依靠操作系统来初始化静态变量是充满了问题的。 要么完全避免它,要么使用一个插件加载器来处理所有特殊情况。

我建议你检查一下glib模块 。 Glib提供了加载动态库的平台独立的方式。 您可以使用这些回调:

  • GmoduleeCheckInit()
  • GmoduleeUnload()

他们可以处理分配和释放任何资源。 而不是依靠操作系统为您以可靠的方式分配静态,您可以动态分配您所需要的。

您只需在动态库中定义这些函数,然后使用以下命令加载和卸载它们:

  • g_module_open()
  • g_module_close()