加载共享库两次

4
我正在尝试在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仅包含对库的一次调用,一切都按预期工作。但是,当我有两个使用相同库的项目时,事情开始变得奇怪。

4
所以你希望共享库不像共享库一样工作;-) 为什么? - P.P
1
编辑您的问题以改进它并解释为什么要两次dlopen同一个库。这听起来像是一些XY问题 - Basile Starynkevitch
1
@BasileStarynkevitch 您是正确的,我很抱歉。我编辑了我的问题,希望我添加了必要的信息。 - MiH
1
关于您对为什么要这样做的描述,看起来似乎该库不是可重入的。当存在可变状态在多个调用之间共享时会发生这种情况。但是,我不排除您调用代码的方式有问题,尽管这种可能性较小,因为您能够通过复制该库来解决问题。 - Ulrich Eckhardt
3个回答

7
您需要使用 dlmopen 来实现此类隔离:
// No need for RTLD_LOCAL, not sure about RTLD_DEEPBIND
lib1 = dlmopen (LM_ID_NEWLM, "mylib.so", RTLD_LAZY | RTLD_DEEPBIND);

3
动态加载代码的整个思想在于您能够与其他进程共享它。因此,我认为真正地重复加载库是不可能的。
不过,有些方法可以解决这个问题。其中一种方法是欺骗动态链接器,使其第二次加载库。复制库是您已经找到的一种方法。我可以想象硬链接也可以起作用。
然而,我认为最好的方法是跟随流程。我看到了两种实现目标的方法:分叉一个单独的进程或创建一个单独的初始化函数。
对于单独的进程,您只需使用适当的IPC机制在父子进程之间设置后,调用fork()即可,而不是第二次加载库。由于fork创建了一个新进程,它接收自己的内存空间,因此所有内容都保持独立。作为IPC,我建议使用某种中间件,例如ZeroMQ、dbus或XMLRPC。
创建单独的初始化函数是另一种选择。为此,您将库的状态组合成结构体,而不是作为全局变量。然后,在该init函数中,创建该结构体的实例,设置它并返回其地址。所有其他函数,以前操作全局状态,现在都将该结构体的地址作为额外参数(通常是第一个参数)接收。而不是加载库两次,您只需调用两次初始化函数即可设置单独的环境。

我目前正在使用pthread来创建每个dlopen()运行的线程。据我所知,fork()和pthreads之间的区别在于fork会创建一个新的地址空间,因此需要IPC中间件。难道进程之间没有直接通信的方法吗? - MiH
1
我认为真的不可能加载两次同一库,但是可以使用 dlmopen 实现。 - yugr
如果 dlmopen 能够正常工作,似乎它是加载该库两次的合适、非 hackish 的方法。 - Ulrich Eckhardt

-1
每次加载库时,是否有更好的方法来拥有单独的地址空间?
实际上,虚拟地址空间 属于一个 进程(因此属于其中所有线程),而不是共享库(它使用该虚拟地址空间的多个段)。
对于 pid 为 1234 的进程,请使用 pmap(1)(如 pmap 1234)或 proc(5)(例如尝试 cat /proc/1234/maps ...)。

你真的应该避免重复dlopen(3)同一个共享库(这是故意设计成困难的;你可以使用符号链接和dlopen打开多个指向同一共享对象的符号链接,但你不应该这样做,例如因为static数据会被“加载两次”,并且会发生后果)。为了避免这种情况发生,动态加载器使用引用计数技术...

还要阅读Drepper的如何编写共享库

是否有更好的方法为每次加载库提供单独的地址空间?

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


你真的应该避免两次dlopen(3)同一个共享库 - 为什么? dlmopen manpage提到了这种情况... - yugr

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接