两次加载共享库

Posted

技术标签:

【中文标题】两次加载共享库【英文标题】:Loading shared library twice 【发布时间】:2018-02-19 10:59:38 【问题描述】:

我试图在 C 中加载一个共享库两次:

lib1 = dlopen("mylib.so", RTLD_LAZY | RTLD_LOCAL | RTLD_DEEPBIND);
lib2 = dlopen("mylib.so", RTLD_LAZY | RTLD_LOCAL | RTLD_DEEPBIND);

我想要的是 lib1 和 lib2 有单独的地址空间,以便他们可以做不同的事情。目前,我能做到这一点的唯一方法是复制 mylib,使代码如下所示:

lib1 = dlopen("mylib.so", RTLD_LAZY | RTLD_LOCAL | RTLD_DEEPBIND);
lib2 = dlopen("mylib2.so", RTLD_LAZY | RTLD_LOCAL | RTLD_DEEPBIND);

在有限的范围内,这对我来说很好。但是,我有一个应用程序使用该库的次数是通用的,这使得复制库很麻烦。

有没有更好的方法让每次加载库时都有一个单独的地址空间?

编辑:

我想多次加载库,因为我的应用程序正在处理一种消息队列。消息队列中的项目引用共享库的名称(例如 mylib)并包含一组应由库处理的数据。我想在多线程环境中处理 MQ,在自己的线程中运行对库方法的每个调用。 只要 MQ 只包含一次对库的调用,一切都会按预期工作。但是,当我有两个项目使用同一个库时,事情就开始变得奇怪了。

【问题讨论】:

所以你想让共享库像共享库一样工作 ;-) 为什么? 编辑你的问题以改进它并解释为什么你想dlopen同一个库两次。闻起来像一些XY problem @BasileStarynkevitch 你是对的,对此我很抱歉。我编辑了我的问题,希望我添加了必要的信息 关于你为什么要这样做的描述,看来图书馆当时不是可重入的。这发生在例如在不同的调用之间共享可变状态。但是,我不排除您调用代码的方式有问题,尽管可能性较小,因为您可以通过复制库来解决它。 【参考方案1】:

您需要使用dlmopen 来实现这种隔离:

// No need for RTLD_LOCAL, not sure about RTLD_DEEPBIND
lib1 = dlmopen (LM_ID_NEWLM, "mylib.so", RTLD_LAZY | RTLD_DEEPBIND);

【讨论】:

这看起来很像我正在寻找的东西。谢谢!【参考方案2】:

动态加载代码的整个想法是您可以共享它,尤其是与其他进程共享。因此,我认为不可能真正加载两次库。

不过有一些方法可以解决这个问题。一种可能是欺骗动态链接器第二次加载它。复制库是您已经找到的一种方法。我也可以想象到工作的硬链接。

但是,我认为如果您使用这里的流程会更好。我看到了两种实现我猜是您的目标的方法:分叉一个单独的进程或创建一个单独的 init 函数。

对于单独的过程,您只需 fork(),在父子之间设置适当的 IPC 机制后,而不是第二次加载库。由于分叉创建了一个新进程,因此它获得了自己的内存空间,并且事情保持独立。作为 IPC,我建议使用某种中间件,例如 ZeroMQ、dbus 或 XMLRPC。

创建一个单独的 init 函数是另一种选择。为此,不是将库的状态创建为全局变量,而是将它们组合成一个结构。然后,在该 init 函数中,创建该结构的一个实例,对其进行设置并返回其地址。以前在全局状态上操作的所有其他函数现在接收该结构的地址作为附加(习惯上的第一个)参数。您只需调用两次 init 函数来设置单独的环境,而不是两次加载库。

【讨论】:

我目前正在使用 pthreads 来创建每个 dlopen() 正在运行的线程。据我了解 fork() 和 pthreads 之间的区别在于 fork 创建了一个新的地址空间,因此需要 IPC 中间件。进程之间没有直接通信的方法吗? “我认为不可能真正加载库两次” - 是,使用dlmopen。 如果 dlmopen 有效,它似乎是一种合适的、非骇客的方法来两次加载所述库。【参考方案3】:

有没有更好的方法让每次加载库时都有一个单独的地址空间?

实际上,virtual address space 属于 process(因此属于其中的所有线程), 属于共享库(它使用 几个虚拟地址空间)。

对于 pid 1234 的进程,使用pmap(1)(如pmap 1234)或proc(5)(例如尝试cat /proc/1234/maps ...)

你真的应该避免dlopen(3)-ing 相同共享库“两次”(这很困难,故意的;你可以使用符号链接和dlopen多个符号链接指向同一个共享对象,但您不应该这样做,例如因为static 数据将被“加载两次”并且会发生后果)。为避免这种情况发生,dynamic loader 使用了reference counting 技术...

另请阅读 Drepper 的 How to Write Shared Libraries

有没有更好的方法让每次加载库时都有一个单独的地址空间?

然后您需要不同的 进程,每个进程都有自己的虚拟地址空间。您将使用inter-process communication:参见pipe(7)、fifo(7)、socket(7)、unix(7)、shm_overview(7)、sem_overview(7) 等...

【讨论】:

“你真的应该避免 dlopen(3)-ing 同一个共享库两次” - 为什么? dlmopen manpage 提到了这个用例...

以上是关于两次加载共享库的主要内容,如果未能解决你的问题,请参考以下文章

nm:共享库符号出现两次或一次

共享库加载时重定位

程序中动态加载共享库

Lampp无法加载共享库

加载共享库的多个副本

Linux之静态库共享库