加载时动态链接和运行时动态链接之间的区别

Posted

技术标签:

【中文标题】加载时动态链接和运行时动态链接之间的区别【英文标题】:Difference between load-time dynamic linking and run-time dynamic linking 【发布时间】:2011-01-04 13:53:12 【问题描述】:

将程序加载到内存时,加载时动态链接和运行时动态链接有什么区别?

【问题讨论】:

【参考方案1】:

加载时链接是指在可执行文件(或其他库)引用的库中的符号在可执行文件/库加载到内存时由操作系统处理。

运行时链接是当您使用操作系统提供的 API 或通过库在需要时加载 DLL 或 DSO,然后执行符号解析。

我比 Windows DLL 更了解 Linux DSO,但原理应该是相同的。 .NET 库可能会有所不同。

在 linux 中,插件架构是这样完成的。您的程序将使用运行时链接来加载库并调用一些函数。然后也许卸载它。它还允许加载具有相同符号的多个库而不会发生冲突。我认为 DLL 将以几乎相同的方式工作。

可执行文件的符号表中有“空格”,需要一些库来填充。这些空格通常在加载时或编译时填充。您可以使用运行时链接来消除符号表中对“空格”的需求。

运行时链接有用的另一个场景是调试库,或在运行时从多个 ABI/API 兼容库中进行选择。我经常有一个库,比如“foo”和一个名为“foo_unstable”的库,并且有一个测试应用程序可以在两者之间切换并进行一些测试。

在 linux 下,要查看可执行文件在加载时链接到哪些库,请运行 ldd 命令并获取输出,例如(在 /bin/ls 上):

linux-vdso.so.1 =>  (0x00007fff139ff000)
librt.so.1 => /lib64/librt.so.1 (0x0000003c4f200000)
libselinux.so.1 => /lib64/libselinux.so.1 (0x0000003c4fa00000)
libcap.so.2 => /lib64/libcap.so.2 (0x0000003c53a00000)
libacl.so.1 => /lib64/libacl.so.1 (0x0000003c58e0000

操作系统将在加载时尝试加载库(.so 文件)。它可能已经在内存中拥有该库。

【讨论】:

【参考方案2】:

Aiden Bell 介绍了基础知识,但我会补充:

加载时动态链接通常通过将您的应用程序静态链接到 .lib.a 文件来完成,该文件包含用于自动建立运行时链接到符号的代码,这些符号可在程序中的 .dll.so 文件中找到启动。这通常用于固定功能(即 C 运行时库等),并允许您的程序从库中的错误修复中获益,同时保持较小的可执行文件大小(通过将通用代码分解到单个库中)。

运行时链接用于更多动态功能,例如插件加载。正如 Aiden 所说,您可以使用LoadLibrary() 或等效的工具在运行时主动将模块附加到您的程序,可能是通过询问包含插件 DLL 的目录,依次加载每个 DLL 并使用本地插件 API 与其对话。通过这样做,您的程序可以加载在编译/链接您的应用程序时甚至不存在的模块,因此可以在部署后有机地增长。

从根本上说,这两种方法最终都会调用LoadLibrary() API,但在前一种情况下使用一组固定的符号和库,在后一种情况下使用更动态的一组。

【讨论】:

+1 用于发展/增长收益。模块化架构很酷。 .Net 可执行文件的附加信息:它们使用运行时动态链接。如果您在“Dependency Walker”中打开 .Net DLL,您可以看到它们仅在加载时动态链接与 MSCOREE.DLL。与此相关的更多信息在这里:***.com/questions/9545603/… 如果您引用但不使用 DLL 并且缺少 DLL,那么您的应用程序将不会出错。您可以在 Debug>Windows>Modules 中查看当前加载的 DLL。 仅供参考:在所有 Unix 上,我知道您将您的应用程序链接到 .a(或 .lib)以“建立到符号的运行时链接.so"。在这些平台上,您可以直接链接到.so。实际上链接到.a,如果存在这样的变体,通常会链接到您通常不想要的静态库变体(例如 Debian 派生发行版上的 Boost 库)。 Windows 是必要的例外情况(和MinGW claims to not even need it every time)。【参考方案3】:

这个问题已经很久没有问过了。艾登和德鲁的回答涵盖了大部分本质。我只是想从程序员的角度补充几点。

如果您使用加载时动态链接,我们必须链接到 LIB 文件。然后在代码中,我们可以像往常一样显式调用该方法。 (代码示例见Using Load-Time Dynamic Linking)

如果您使用运行时动态链接,您必须自己管理 DLL 加载/释放和函数查找。 (代码示例见Using Run-Time Dynamic Linking)

要在 2 个选项之间进行选择,请查看 Determining Which Linking Method to Use。

所以,我认为加载时动态链接只是另一种节省程序员工作量的方法。但它是以一些可扩展性为代价的。您只能使用与您用作导入库的 LIB 文件对应的 DLL。

从根本上说,两种链接方法都使用 Windows 平台上的 LoadLibrary() API。

【讨论】:

【参考方案4】:

在加载时动态链接可执行文件链接到 DLL 库,而在运行时动态链接中没有任何可执行文件链接到任何 DLL。

当应用程序的启动性能很重要时,运行时动态链接更可取

【讨论】:

【参考方案5】:

加载时间过早地优化了GetProcAddress(),方法是从 DLL 的开头创建一个固定的偏移量。较旧的可执行文件不能与违反 SOLID 开放原则的较新 DLL 一起使用;较新的可执行文件不能与较旧的 DLL 一起使用,因为函数偏移量可能不同,因此违反了 SOLID 的关闭原则。当你违反 SOLID 时,你会得到 DLL-HELL。

运行时不能过早优化掉GetProcAddress() 调用。较旧的可执行文件可以与较新的 DLL 一起使用,但不能使用遵循 SOLID 关闭原则的新功能;较新的可执行文件可以与较旧的 DLL 一起使用,但不能使用遵循 SOLID 关闭原则的新功能。将旧的可执行文件与旧的 DLL 使用,以及将新的可执行文件与新的 DLL 使用的比较是对 SOLID 开放原则的遵守。

Hot Code ReloadingObject Oriented Programming。你失败了Liskov Substitution Principle,较新的 DLL 不能与较旧的可执行文件一起使用,或者较旧的 DLL 不能与较新的可执行文件一起使用。新版本是旧版本的inheritance,无论它们是可执行文件还是 DLL。

【讨论】:

以上是关于加载时动态链接和运行时动态链接之间的区别的主要内容,如果未能解决你的问题,请参考以下文章

彻底搞懂程序链接过程之动态链接

动态链接及静态链接

Windows API编程之动态链接库(DLL)

[转载] 动态链接库dll的 静态加载 与 动态加载

在 C++ 程序上使用加载时或运行时动态链接时,如何在编译时解析引用?

动态链接库的加载