8

我试图弄清楚操作系统如何处理加载相同 DLL/共享库的多个不相关进程。我关心的操作系统是 Linux 和 Windows,但在较小程度上也是 Mac。我认为我的问题的答案对于所有操作系统都是相同的。

我对显式链接特别感兴趣,但我也想知道隐式链接。我认为两者的答案也将是相同的。

这是迄今为止我发现的关于 Windows 的最佳解释:

“系统在所有加载的模块上维护每个进程的引用计数。调用 LoadLibrary 会增加引用计数。调用 FreeLibrary 或 FreeLibraryAndExitThread 函数会减少引用计数。系统在其引用计数达到零或进程终止时卸载模块(无论参考计数如何)。” - http://msdn.microsoft.com/en-us/library/windows/desktop/ms684175%28v=vs.85%29.aspx

但它留下了一些问题。

1.) 不相关的进程是否会冗余地加载同一个 DLL(即 DLL 在内存中存在多次)而不是使用引用计数?(IE,进入每个进程自己的“地址空间”,我想我理解它)

如果在进程终止后立即卸载 DLL,这使我相信使用完全相同 DLL 的其他进程将冗余加载到内存中,否则不应允许系统忽略引用计数。

2.) 如果这是真的,那么当您在同一进程中多次加载 DLL 时,引用计数 DLL 的意义何在?将同一个 DLL 两次加载到同一个进程有什么意义?我能想到的唯一可行的原因是,如果一个 EXE 引用了两个 DLL,而其中一个 DLL 引用了另一个,那么同一个库将至少有两个 LoadLibrar() 和两个 FreeLibrary() 调用。

我知道我似乎在这里回答我自己的问题,但我只是假设。我很想知道。

4

1 回答 1

6

共享库或 DLL 将为代码部分加载一次,并为任何可写数据部分加载多次[可能通过“写时复制”,所以如果你有一大块内存,大部分是读取的,但有些小正在编写的部分,所有 DLL 都可以使用相同的部分,只要它们没有从原始值更改]。

然而,一个 DLL 可能会被多次加载。加载 DLL 时,会加载一个基地址,即代码开始的位置。如果我们有某个进程正在使用,比如说,两个 DLL,由于它们先前的加载,使用相同的基地址 [因为使用它的其他进程不会同时使用这两个],那么 DLL 中的一个将不得不再次加载到不同的基地址。对于大多数 DLL,这是相当不寻常的。但它可能发生。

每次加载引用计数的意义在于,它允许系统知道何时可以安全卸载模块(当引用计数为零时)。如果我们有系统的两个不同部分,都想使用同一个 DLL,并且它们都加载该 DLL,那么当系统的第一部分关闭 DLL 时,您真的不希望导致系统崩溃。但是我们也不希望在系统的第二部分关闭 DLL 时 DLL 留在内存中,因为那样会浪费内存。[想象一下,这个应用程序是一个在服务器上运行的进程,每周都会从服务器上下载新的 DLL,因此每周都会加载“最新”的 DLL(名称不同)。几个月后,你的整个内存中就会充满这个应用程序“旧的、未使用的”DLL]。LoadLibrary调用,主可执行文件加载相同的 DLL。同样,您确实需要两次FreeLibrary调用才能关闭它。

于 2013-06-28T23:31:12.250 回答