【问题标题】:Is it possible to keep DLL in memory after calling process exits?调用进程退出后是否可以将DLL保留在内存中?
【发布时间】:2012-06-08 16:54:56
【问题描述】:

我有一个 DLL 需要 5 到 10 秒才能加载,这意味着我每次编译和运行使用它的可执行文件时都必须等待很长时间。有没有办法让 DLL 加载在内存中,以便每次编译相应的可执行文件时都可以立即访问它?如果相关的话,我正在 QT MinGW 上编译程序。

编辑:到目前为止还没有运气。在另一个程序上加载 DLL 似乎没有任何效果(原始程序仍然加载 DLL,并且需要同样长的时间)。如果将 DLL 及其函数加载到另一个程序中,我想我需要以不同的方式加载它们,但我不知道该怎么做。现在我正在使用 LoadLibrary 和 GetProcAddress。

【问题讨论】:

  • 可能不是加载到内存,而是 DLL 内部的初始化逻辑,这需要很长时间。将 DLL 保存在内存中并不能避免初始化延迟。
  • 如果属实,那将是不幸的。如果不访问源代码,我认为我无能为力?
  • 你可以将它包装到一个进程外的 RPC 服务器中,但这会以快速重新连接换取使用它的低效率。
  • Ben Voigt 的回答很可能是正确的。硬盘的读取速度约为 40-80 MB/s。因此,除非您的 dll 真的很大(200-400 MB),否则时间不会花在从磁盘加载 dll 上。在您的other question 中,您提到了一个大小为 5 MB 的 dll。该 dll 似乎处理一些外部硬件,所以时间很可能花在检测或初始化硬件上。您可以尝试联系 dll 供应商并询问他们是否可以对开始时间做些什么。
  • 没有办法让 DLL 被我选择运行的任何程序初始化和使用?换句话说,运行加载和初始化 DLL 的程序 A,然后从实际使用 DLL 上的函数的那一点运行程序 B(我问的是这是否可以通常完成,而不是针对这个特定的库)。我通过链接到调用它的程序来意识到 DLL 的功能,所以上述情况不太可能,但我想我会问以防万一。

标签: c++ qt dll


【解决方案1】:

创建一个显式安装的系统服务,以保持您的 DLL 加载。这样初始化会在启动时发生,并且永远不会再发生。我建议不要使用大多数其他答案中概述的方法。虽然它们看起来像是在工作,但在我看来,它们就像是您的软件的不良行为。从用户和维护者的角度来看,我更喜欢显式安装的系统服务,而不是将某些东西挂接到 winlogin.exe 中。您越诚实地使用 Windows API 和环境,跨版本和版本升级时的重大更改就越少。

【讨论】:

  • 确实是这样,所以我编辑掉了我的“不”。不过,我对这些解决方案感到不安。
  • 对附加到 DLL 的每个进程和线程进行初始化。将 DLL 加载到服务中只会保存文件 I/O。
  • 您能否详细说明如何执行此操作?就目前而言,您的回答主要是对所提供的其他建议的批评。
  • @SharpHawk:如果关注的是编译迭代时间,则没有真正需要提供服务。按照 user877329 的建议,只需编写一个程序并将其保持打开状态。系统服务更常用于在部署的客户环境中将 DLL 保存在内存中。例如,SeaMonkey 浏览器就是这样做的,一些办公套件也是如此。然而,这都是谎言,实际上并没有让事情变得更快,而只是掩盖了计算机运行缓慢的原因,因此客户不知道应该归咎于哪个软件。
  • 鉴于上述情况以及这个答案建议在启动时进行加载,而不是第一次实际需要加载,我必须 -1。
【解决方案2】:

如果我没记错的话,Windows 会在内存中保留一个 DLL 实例,因此保持其活动状态应该可以:

#include <conio.h>
#include <windows.h>

int main()
    {
    HMODULE handle=LoadLibrary("yourdll.dll");
//  Make shure Windows resolves the DLL
    FARPROC dummy=GetProcAddress(handle,"functionInDll");
//  The process will now just wait for keyboard input.
    getch();
    CloseHandle(handle);
    return 0;
    }

【讨论】:

  • 你为什么要做 FARPROC dummy=GetProcAddress(handle,"functionInDll");如果你只想保持 DLL 加载?
  • 嗯,我尝试让我的程序实例保持打开状态(加载了 DLL),但较新的程序实例仍然需要相同的时间来加载 DLL。使用函数本身总是即时的。我想知道问题是什么......
【解决方案3】:

最简单的解决方案是(假设 MSVC++)使 DLL 延迟加载。权衡当然是初始化仍然必须发生,但这将不再延迟程序的其他部分。例如。您可以在后台线程上执行此操作。

【讨论】:

  • 鉴于我无法让 DLL 留在内存中,并且 totowtwo 的解决方案意味着进一步降低廉价笔记本电脑的速度,在并行线程中加载 DLL 似乎是走。我在 StackOverflow 上提出的一个相关问题也提出了相同的解决方案。
【解决方案4】:

我不是 MinGW 开发人员,但您的问题很常见,并不取决于您如何创建 DLL。此类问题通常使用以下三种技术来解决:

  • 选择 DLL 的唯一基地址
  • 绑定 DLL 和 exe(在安装应用程序期间)
  • DLL 延迟加载技术的使用
  • DLL_PROCESS_ATTACH 部分DllMain 中调用DisableThreadLibraryCalls 稍微提高加载时间

您可以使用的链接器或其他工具的确切切换取决于您的开发环境。

要了解问题,您应该知道如何加载可执行文件或 DLL。首先将EXE或DLL映射到内存中。将创建指向 EXE/DLL 的Memory mapped file(部分)。因此,您将在进程中拥有一些地址,这些地址将对应于 EXE/DLL 文件。如果链接 DLL,则可以选择基地址。如果该地址在进程地址空间中未使用,则不会执行任何操作。如果将使用第一行代码(您从 DLL 中调用某个函数),则使用地址附近的内存页面 8K 将从文件加载到内存中。如果两个进程使用相同的 DLL,则代码的物理内存将在进程之间共享。即使您持有已初始化的变量,带有变量的页面也将被共享,直到变量的第一次更改。修改时将为进行修改的进程制作内存页的副本。

在进程中加载​​ DLL 后,必须修改调用者的一些小部分(例如 EXE)以包含 DLL 中使用的函数的实际地址。使用来自另一个 DLL 的函数的 DLL 也会这样做。

一切听起来都很完美,但是如果您在 DLL 编译期间没有设置任何链接器选项(如果您不使用 --image-base--enable-auto-image-base 链接器选项),您的所有 DLL 将具有相同的基地址(默认链接器的值)。所以第一个 DLL 可以在该地址加载。在加载与相同(或某些重叠地址)链接的第二个 DLL 期间,将完成 DLL 的重定位。在重定位期间,DLL 的代码会被修改,因此 1) DLL 的加载会很慢 2) 代码的修改副本将在进程中生成(包括 DLL 使用的内存) 3) 修改后的副本不会在 DLL 的多个实例之间共享(即使所有实例都将以相同的方式修改)。

我建议您首先使用Process Explorer 来验证哪些DLL 将在您的应用程序中重定位。您应该在“视图”/“下疼痛视图”菜单中选择“DLLs”选项,并在“选项”菜单的“配置突出显示”中选择“重定位 DLLs”复选框。您还可以自定义将显示每个 DLL 的哪些信息。像下面这样的信息越多,您将看到程序加载越慢,并且在您的应用程序实例之间或使用相同 DLL 的不同应用程序之间不会共享更多的地址空间:

在上面的示例中,您看到树 Lenovo DLL TPOSDSVC.dllHKVOLKEY.dllTPLHMM.dll 链接到相同的基地址 0x10000000,并且只有一个 DLL(此处为 TPOSDSVC.dll)将加载到该地址.其他两个 DLL 必须重新定位。

我不能在这里写一本关于这个主题的书。我建议您检查有关重定位问题的应用程序。您可以使用链接器选项(--image-base--enable-auto-image-base 似乎是您所需要的)。您可以使用dumpbin.exe 工具(来自免费版的 Visual Studio)来检查 PE 映像。

在您的所有 DLL 将具有唯一的基地址之后,您可以使用另一个工具 bind.exe 和选项 -u 将 EXE 和您的 DLL 绑定到其依赖的 DLL。它将另外减少内存大小并改善应用程序的启动时间。它将更新您的 DLL 和 EXE 的 IMAGE_DIRECTORY_ENTRY_IMPORTIMAGE_DIRECTORY_ENTRY_DELAY_IMPORT 部分(请参阅 the answer)。 Bind.exe 在内部使用 BindImageEx API。许多 Windows Installer 安装程序使用BindImage 操作和BindImage 表在 EXE 和 DLL 安装结束时进行绑定。

您可以考虑使用其他技术(请参阅here)来减小 DLL 和 EXE 的大小。

我不确切知道如何在 MinGW 中使用延迟加载技术,但它应该是绝对可行的。您需要在 Visual Studio 中执行两个步骤:包含 Delayimp.lib 作为附加库并使用 /DELAYLOAD 选项(请参阅 here)指定应在第一次使用时而不是直接加载 DLL 中的哪些。使用非常有用的工具Dependency Walker,您可以看到大多数标准 Microsoft DLL 都使用了该技术。如果您也使用该技术,您可以改善应用程序的启动时间并减少使用的内存。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-07-10
    • 1970-01-01
    • 1970-01-01
    • 2020-04-12
    • 1970-01-01
    相关资源
    最近更新 更多