为什么 LIB 和 DLL 不能互换?

Why aren't LIBs and DLLs interchangeable?

LIB 文件是必须在编译时包含的静态库,而 DLL 文件可以在运行时由程序“动态”访问。 (但是,DLL 必须在运行前使用导入库 (LIB) 隐式链接或通过 LoadLibrary 显式链接)。

我的问题是:为什么要区分这些文件类型?为什么不能将 LIB 文件视为 DLL,反之亦然?这似乎是一个不必要的区别。

一些相关问题:

DLL and LIB files - what and why?

Why are LIB files beasts of such a duplicitous nature?

DLL and LIB files

What exactly are DLL files, and how do they work?

您必须区分可共享对象和静态库,因为它们是完全不同的对象。

可共享目标文件,作为 DLL 或 SO,包含加载器使用的结构,允许动态 link 进出其他 executable 图像(即导出 tables).

一个 DLL 完全是一个 executable 图像,作为一个 executable,可以加载到内存中并重新定位(如果不是位置无关的代码),但不仅可以导入符号,就像 executable 所做的那样,但也暴露了导出的符号。

加载程序可以使用导出的符号在内存中插入link 个不同的 executable 模块。

静态库,另一方面,只是对象模块的集合,要在单个 executable 中 linked,或者甚至一个 DLL。

目标模块包含指令字节码和通过重定位引用的外部符号的占位符table。

linker 每次引用对象模块时一个一个地收集对象模块,即函数调用,并将对象添加到 linking 代码流中,然后检查重定位 table 的目标模块,并将每个外部符号的出现替换为 linked 代码中符号的位移。随着新引用的发现,最终添加更多的对象模块。这是一个递归过程,当不再有未定义的引用时将结束。

在 linking 过程结束时,您在内存中有 想象 的执行 table 代码。此图像将由 loader(一个 OS 组件)读取并放置在内存中,它将修复一些次要引用并使用地址填充导入 table从 DLL 导入的符号。

此外,如果您确实可以从存档(库文件)中提取所需的每个目标模块,那么您就不能从 DLL 中提取单个部分,因为它是所有模块的合并,没有任何参考每个的开始和结束。

现在应该清楚了,虽然目标模块 .obj 文件或它们的集合 .lib 文件与 DLL 有很大不同。第一个是原始代码,第二个是完全 linked 和“准备 运行”的代码片段。

共享对象和静态库存在的根本原因是效率和资源合理化

当您静态 link 库模块时,您会为使用该静态库创建的每个 executable 复制相同的代码,这意味着更大的 executable 文件需要更长的加载时间浪费内核执行时间和内存 space.

当你使用可共享对象时,你只在第一次加载代码,然后对于所有后续的executables,你只需要映射新进程中DLL代码所在的space内存 space 并创建一个新的数据段(这对于每个进程来说必须是唯一的以避免冲突),有效地优化内存和系统使用(对于较轻的加载器工作负载)。

那么我们要如何在两者之间做出选择呢?

静态 link 当您的代码仅供有限数量的程序使用时,静态 linking 很方便,在这种情况下,加载单独的 DLL 模块的努力是不值得的。

Static linking 还允许轻松引用流程定义的全局变量或其他流程本地数据。这对于 DLL 是不可能的,或者不是那么容易,因为作为一个完整的 executable 不能有未定义的引用所以 你必须在 DLL 中定义任何全局,并且这个引用将是通用的对于访问 DLL 代码的所有进程.

动态 link 当代码被许多程序使用时,动态 linking 很方便,可以提高加载程序的工作效率,并减少内存使用。这方面的例子是几乎所有程序或编译器都使用的系统库 运行time.