加载共享库的多个副本

Posted

技术标签:

【中文标题】加载共享库的多个副本【英文标题】:Load multiple copies of a shared library 【发布时间】:2009-11-17 01:05:45 【问题描述】:

我正在运行 Linux,我希望能够对共享库 (.so) 进行并行函数调用,不幸的是它不是线程安全的(我猜它具有全局数据结构)。

出于性能原因,我不想简单地将函数调用包装在互斥体中。

我想做的是产生 4 个线程,并将同一个库的 4 个副本加载到进程内存中。然后每个线程将函数调用到它自己的库副本中。

不幸的是,dlopen 不允许我加载任何库的多个实例。

有谁知道可以让我多次加载库的方法吗? (除了制作 .so 文件的 4 个副本,每个副本都有不同的名称)

【问题讨论】:

对于需要示例的,实现了比较here。 【参考方案1】:

可以像这样加载库的多个独立副本:

#define _GNU_SOURCE
#include <dlfcn.h>
...
void *handle = dlmopen(LM_ID_NEWLM, "/path/to/library.so", RTLD_NOW);

更多信息here。

【讨论】:

对我们这些可怜的 Windows 用户有什么想法吗?不过,这对 linux 非常有用。 这在 Windows 中也可以使用 LoadLibrary 和 GetProcAddress。我认为您需要拥有 DLL 的多个物理副本,但它工作正常。 谢谢!这让你可以做一些疯狂的事情,比如在 C++ 类中包装一个非线程安全的 C 库。类的 new() 运算符使用 LM_ID_NEWLM 加载库,然后使用 dlsym() 将库的函数指针填充为类中的字段。像往常一样使用 C++ 类。有点邪恶,但当你不想重构来自地狱深处的人的 10,000 行代码以使用静态共享状态时,它会起作用。 dlmopen() 可能会错过的一个限制是(截至今天),glibc 实现仅限于每个进程 16 个命名空间。每次您使用 LM_ID_NEWLM 调用 dlmopen() 时,都会使用其中一个命名空间(直到您卸载该命名空间中的所有模块)。【参考方案2】:

您可以使用多个进程,而不是使用线程,每个进程都做一些工作。这在 *nix 上很常见,而且通常更容易编码。

【讨论】:

我不知道更容易编码。当然,调用fork() 比启动线程要容易一些。但是,如果您需要在进程之间进行通信,或者在进程之间共享数据,那么您就进入了 IPC 的世界(共享内存、管道等),这可以说是更复杂的代码。 对于初学者来说,使用他的非线程安全库更容易。 :) 我发现编码更容易,因为它更容易避免死锁和相关问题,但是,是的,它们在 IPC 中也有它们的版本。 @Charles - 考虑到 OP 的目标,多进程可能是他最不痛苦的选择。 @Charles:这一切都取决于您的线程/进程之间的共享数据量。使用流程的好处是它可以通过良好控制的通信系统进行良好的程序设计。线程表面上的简单性通常是糟糕的设计。 Boost.Interprocess 极大地有助于共享内存...但当然理想情况下,您希望只给进程一个自己的副本,然后等待结果。【参考方案3】:

看起来是个坏主意。共享库不再像静态库那样实现。

您可能可以将 dlopen() 与 RTLD_LOCAL 标志一起使用,这样对 dlopen 的后续调用就不会看到它已经加载并使其按您想要的方式工作......但它看起来仍然是一个糟糕的设计理念。如果您遇到性能问题,最好避免将同一库的多个副本弄乱内存。

我建议要么使用多个进程,要么采用互斥方式,这可能更有效。

当您在 Linux 上工作时,如果您可以访问库的源代码,也可能存在其他方法,例如重命名它的符号以根据需要拥有尽可能多的单独实例......好吧,一旦您获得源代码,可能会有其他方式,例如使库线程安全。

【讨论】:

【参考方案4】:

它是什么库?是不是很大的东西?我想知道您是否无法以某种方式将库修复为线程安全的,然后使用该库的线程安全版本构建您的代码。这取决于库的大小和问题所在,但如果您可以修复库,您就可以按照自己的方式构建应用,并帮助其他人。

【讨论】:

为什么你的措辞就像暴露非线程安全的 API 是错误的,需要修复?提供线程安全保证和不提供线程安全保证都需要权衡取舍。 @ulidtko - 最初的问题是关于以缺乏线程安全性会破坏事物的方式使用非线程安全 API。对于原始海报的目的,图书馆被打破了。显然,非线程安全 API 没有问题,但对于 OP,需要在库可用之前进行修复。这是一个上下文。 @ulidtko - 另外,我们不知道为什么这个库不是线程安全的。代码不是线程安全的有很多很好的理由(例如,STL 容器不是线程安全的),但也有很多不太好的理由(例如,旧的 C strtok 使用内部静态指针,它也许在当时是有道理的,但从今天的角度来看看起来很愚蠢)。

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

如何为具有共享代码库的多个项目正确设置 git?

静态库和共享库

如何以编程方式查找已加载共享库的版本?

程序中动态加载共享库

如何使用 dlmopen 在不同的线程中打开多个共享库?

Linux之静态库共享库