问答中心分类: C++共享对象 (.so)、静态库 (.a) 和 DLL (.so) 之间的区别?
0
匿名用户 提问 17小时 前

我参与了一些关于 Linux 库的辩论,并想确认一些事情。
据我了解(如果我错了,请纠正我,稍后我会编辑我的帖子),在构建应用程序时有两种使用库的方法:

  1. 静态库(.a 文件):在链接时,将整个库的副本放入最终应用程序中,以便库中的函数始终可供调用应用程序使用
  2. 共享对象(.so 文件):在链接时,对象只是通过相应的头文件 (.h) 对其 API 进行验证。该库直到运行时才真正使用,在需要它的地方。

静态库的明显优势是它们允许整个应用程序自包含,而动态库的好处是可以替换“.so”文件(即:如果由于安全原因需要更新它bug) 无需重新编译基础应用程序。
我听说有些人区分共享对象和动态链接库(DLL),即使它们都是“.so”文件。在 Linux 或任何其他符合 POSIX 的操作系统(即:MINIX、UNIX、QNX 等)上进行 C/C++ 开发时,共享对象和 DLL 之间有什么区别吗?有人告诉我,一个关键的区别(到目前为止)是共享对象只在运行时使用,而 DLL 必须首先使用应用程序中的 dlopen() 调用打开。
最后,我还听到一些开发人员提到“共享档案”,据我了解,它本身也是静态库,但从未被应用程序直接使用。相反,其他静态库将链接到“共享档案”,以将一些(但不是全部)功能/资源从共享档案中提取到正在构建的静态库中。
预先感谢大家的帮助。
更新

在向我提供这些术语的上下文中,实际上是一组必须学习 Linux 的 Windows 开发人员使用的错误术语。我试图纠正它们,但(不正确的)语言规范卡住了。

  1. 共享对象:程序启动时自动链接到程序中的库,并作为独立文件存在。该库在编译时包含在链接列表中(即:LDOPTS+=-lmylib对于名为的库文件mylib.so)。该库必须在编译时以及应用程序启动时存在。
  2. 静态库:在构建时合并到实际程序本身的库,用于单个(更大)应用程序,其中包含应用程序代码和在构建程序时自动链接到程序中的库代码,以及包含两者的最终二进制文件主程序和库本身作为一个独立的二进制文件存在。该库在编译时包含在链接列表中(即:LDOPTS+=-lmylib对于名为的库文件mylib.a)。该库必须在编译时存在。
  3. DLL:本质上与共享对象相同,但不是在编译时包含在链接列表中,而是通过以下方式加载库dlopen()/dlsym()命令,以便库在编译时不需要在构建时出现。此外,在应用程序启动或编译时不需要(必然)存在该库,因为它只在此刻需要dlopen/dlsym调用。
  4. 共享存档:本质上与静态库相同,但使用“export-shared”和“-fPIC" 标志。该库在编译时包含在链接列表中(即:LDOPTS+=-lmylibS对于名为的库文件mylibS.a)。两者之间的区别在于,如果共享对象或 DLL 想要将共享存档静态链接到它自己的代码中并且能够使共享对象中的函数对其他程序可用,而不仅仅是使用它们,则需要这个附加标志DLL 内部。这在有人为您提供静态库并且您希望将其重新打包为 SO 的情况下很有用。该库必须在编译时存在。

额外更新
"之间的区别DLL“ 和 ”shared library“在我当时工作的公司中,这只是一种(懒惰、不准确的)俗语(Windows 开发人员被迫转向 Linux 开发,并且这个词被卡住了),符合上述描述。
此外,尾随“S" 图书馆名称后的文字,在“共享档案”的情况下,只是该公司使用的惯例,而不是一般行业中的惯例。

Some programmer dude 回复 17小时 前

为了.a文件,“a”实际上代表“archove”,它只是一个目标文件的存档。现代链接器应该足够好,不需要包含 while 库,只需要包含存档中需要的目标文件,甚至可能只使用引用的目标文件中的代码/数据部分。

Some programmer dude 回复 17小时 前

DLL 只是 Windows 术语。它不用于unices。

Some programmer dude 回复 17小时 前

@Someprogrammerdude 你的意思是“拱门”吗?ve" 或 "arch一世ve"。我用谷歌搜索了前者,但找不到任何涉及该字面术语的文献。谢谢!

Some programmer dude 回复 17小时 前

@DevNull "拱门一世ve”当然。:)

4 Answers
0
aleroot 回答 17小时 前

一个静态库(.a)是一个可以直接链接到链接器生成的最终可执行文件的库,它包含在其中,不需要将库放入将部署可执行文件的系统中。
一个共享库(.so)是一个链接但未嵌入最终可执行文件中的库,因此将在启动可执行文件时加载,并且需要存在于部署可执行文件的系统中。
一个windows上的动态链接库(.dll)就像 linux 上的共享库(.so),但与操作系统(Windows 与 Linux)相关的两种实现之间存在一些差异:
一个DLL可以定义两种函数:exported 和 internal。导出的函数旨在由其他模块调用,以及从定义它们的 DLL 中调用。内部函数通常只能从定义它们的 DLL 中调用。
一个所以Linux 上的库不需要特殊的导出语句来指示可导出符号,因为所有符号都可用于查询过程。

Mike 回复 17小时 前

+1很好的简单解释。如果一个函数在 DLL 中声明为“内部”,这是否意味着它不能从图书馆外面打电话?

Zan Lynx 回复 17小时 前

不一定所有符号都在 SO 库中可用。隐藏符号是可能的,建议使用,因为图书馆用户没有充分的理由看到您的所有符号。

Brian Cannard 回复 17小时 前

仅供参考:g++ 有__attribute__选择性“导出”符号的语法:#define DLLEXPORT __attribute__ ((visibility("default"))) #define DLLLOCAL __attribute__ ((visibility("hidden")))

0
JoGusto 回答 17小时 前

我可以详细说明 Windows 中 DLL 的详细信息,以帮助我在 *NIX 领域的朋友澄清这些谜团……
DLL 类似于共享对象文件。两者都是图像,准备好由各自操作系统的程序加载器加载到内存中。这些图像伴随着各种元数据,以帮助链接器和加载器进行必要的关联并使用代码库。
Windows DLL 有一个导出表。导出可以按名称或按表位置(数字)。后一种方法被认为是“老派”并且更加脆弱——重建 DLL 和更改函数在表中的位置将以灾难告终,而如果通过名称链接入口点则没有真正的问题。因此,请忘记这是一个问题,但请注意,如果您使用“恐龙”代码(例如 3rd-party 供应商库),它就在那里。
Windows DLL 是通过编译和链接构建的,就像您对 EXE(可执行应用程序)所做的那样,但 DLL 不是独立的,就像 SO 旨在由应用程序使用一样,通过动态加载或通过链接时绑定(对 SO 的引用嵌入在应用程序二进制文件的元数据中,并且 OS 程序加载器将自动加载引用的 SO)。 DLL 可以引用其他 DLL,就像 SO 可以引用其他 SO一样。
在 Windows 中,DLL 将只提供特定的入口点。这些被称为“出口”。开发人员可以使用特殊的编译器关键字使符号外部可见(对其他链接器和动态加载器),或者可以在模块定义文件中列出导出,该文件在链接时使用 DLL 本身正在创建。现代的做法是用关键字装饰函数定义以导出符号名称。还可以创建带有关键字的头文件,这些关键字将声明该符号为要从当前编译单元之外的 DLL 导入的符号。查找关键字 __declspec(dllexport) 和 __declspec(dllimport) 以获取更多信息。
DLL 的一个有趣特性是它们可以声明一个标准的“加载/卸载”处理函数。每当加载或卸载 DLL 时,DLL 都可以执行一些初始化或清理,视情况而定。这很好地映射为将 DLL 作为面向对象的资源管理器,例如设备驱动程序或共享对象接口。
当开发人员想要使用已构建的 DLL 时,她必须引用 DLL 开发人员在创建 DLL 时创建的“导出库”(*.LIB),或者必须在运行时显式加载 DLL 并请求通过 LoadLibrary() 和 GetProcAddress() 机制按名称命名的入口点地址。大多数时候,链接到一个 LIB 文件(它只包含 DLL 的导出入口点的链接器元数据)是使用 DLL 的方式。动态加载通常保留用于在程序行为中实现“多态性”或“运行时可配置性”(访问附加组件或后来定义的功能,即“插件”)。
Windows 的做事方式有时会引起一些混乱;系统使用 .LIB 扩展名来引用普通静态库(存档,如 POSIX *.a 文件)和在链接时将应用程序绑定到 DLL 所需的“导出存根”库。因此,应始终查看 *.LIB 文件是否具有同名的 *.DLL 文件;如果不是,那么 *.LIB 文件很有可能是一个静态库存档,而不是导出 DLL 的绑定元数据。

0
rapadura 回答 17小时 前

你是对的静态文件在链接时复制到应用程序, 然后共享文件只是在链接时验证并在运行时加载.
dlopencall 不仅适用于共享对象,如果应用程序希望在运行时代表它这样做,否则共享对象会在应用程序启动时自动加载。 DLLS 和.so是一回事。这dlopen存在为流程添加更细粒度的动态加载能力。你不必使用dlopen自己打开/使用 DLL,这在应用程序启动时也会发生。

Cloud 回复 17小时 前

使用 dlopen() 进行更多加载控制的一个例子是什么?例如,如果 SO/DLL 在启动时自动加载,dlopen() 是否会以不同的权限或限制关闭并重新打开它?谢谢你。

rapadura 回复 17小时 前

我相信 dlopen 是用于插件或类似功能的。权限/限制应该与自动加载相同,无论如何 dlopen 将递归加载依赖库。

Basile Starynkevitch 回复 17小时 前

DLL 和.so不完全是同样的事情。看这个答案

0
Raymond Jennings 回答 17小时 前

我怀疑这里存在某种误解,但是头文件,至少是用于编译源代码的 .h 文件,在链接时绝对不会被检查。
.h 和 .c/.cpp 文件仅在编译阶段涉及,其中包括预处理。一旦创建了目标代码,在链接器开始处理事情之前,头文件早就消失了。