我参与了一些关于 Linux 库的辩论,并想确认一些事情。
据我了解(如果我错了,请纠正我,稍后我会编辑我的帖子),在构建应用程序时有两种使用库的方法:
- 静态库(.a 文件):在链接时,将整个库的副本放入最终应用程序中,以便库中的函数始终可供调用应用程序使用
- 共享对象(.so 文件):在链接时,对象只是通过相应的头文件 (.h) 对其 API 进行验证。该库直到运行时才真正使用,在需要它的地方。
静态库的明显优势是它们允许整个应用程序自包含,而动态库的好处是可以替换“.so”文件(即:如果由于安全原因需要更新它bug) 无需重新编译基础应用程序。
我听说有些人区分共享对象和动态链接库(DLL),即使它们都是“.so”文件。在 Linux 或任何其他符合 POSIX 的操作系统(即:MINIX、UNIX、QNX 等)上进行 C/C++ 开发时,共享对象和 DLL 之间有什么区别吗?有人告诉我,一个关键的区别(到目前为止)是共享对象只在运行时使用,而 DLL 必须首先使用应用程序中的 dlopen() 调用打开。
最后,我还听到一些开发人员提到“共享档案”,据我了解,它本身也是静态库,但从未被应用程序直接使用。相反,其他静态库将链接到“共享档案”,以将一些(但不是全部)功能/资源从共享档案中提取到正在构建的静态库中。
预先感谢大家的帮助。
更新
在向我提供这些术语的上下文中,实际上是一组必须学习 Linux 的 Windows 开发人员使用的错误术语。我试图纠正它们,但(不正确的)语言规范卡住了。
- 共享对象:程序启动时自动链接到程序中的库,并作为独立文件存在。该库在编译时包含在链接列表中(即:
LDOPTS+=-lmylib
用于名为mylib.so
的库文件)。 该库必须在编译时以及应用程序启动时存在。 - 静态库:在构建时合并到实际程序本身的库,用于单个(较大)应用程序,包含应用程序代码和在构建程序时自动链接到程序中的库代码,以及包含两者的最终二进制文件主程序和库本身作为一个独立的二进制文件存在。该库在编译时包含在链接列表中(即:
LDOPTS+=-lmylib
用于名为mylib.a
的库文件)。 该库必须在编译时存在。 - DLL:本质上与共享对象相同,但不是在编译时包含在链接列表中,而是通过
dlopen()
/dlsym()
命令加载库,因此库不会需要在构建时出现才能编译程序。 此外,在应用程序启动或编译时不需要(必然)存在该库,因为它仅在dlopen
/dlsym
调用时需要。 - 共享存档:本质上与静态库相同,但使用“export-shared”和“
-fPIC
”标志编译。该库在编译时包含在链接列表中(即:LDOPTS+=-lmylibS
对于名为mylibS.a
的库文件)。两者之间的区别在于,如果共享对象或 DLL 想要将共享存档静态链接到它自己的代码中并且能够使共享对象中的函数对其他程序可用,而不仅仅是使用它们,则需要这个附加标志DLL 内部。这在有人为您提供静态库并且您希望将其重新打包为 SO 的情况下很有用。 该库必须在编译时存在。
额外更新
“ DLL
”和“ shared library
”之间的区别只是我当时工作的公司的一种(懒惰,不准确的)俗语(Windows开发人员被迫转向Linux开发, 和术语卡住), 遵守上面提到的描述。
此外,在“共享档案”的情况下,库名称后面的尾随“ S
”文字只是该公司使用的惯例,而不是一般行业中的惯例。
原文由 Cloud 发布,翻译遵循 CC BY-SA 4.0 许可协议
我一直认为 DLL 和共享对象只是同一事物的不同术语 - Windows 称它们为 DLL,而在 UNIX 系统上它们是共享对象,通用术语 - 动态链接库 - 涵盖两者(甚至函数在 UNIX 上打开一个 .so 被称为
dlopen()
在“动态库”之后)。它们确实只在应用程序启动时链接,但是您对头文件进行验证的概念是不正确的。头文件定义了编译使用库的代码所需的原型,但在链接时,链接器会查看库本身以确保它需要的函数确实存在。链接器必须在链接时在某处找到函数体,否则会引发错误。它还在运行时执行此操作,因为正如您正确指出的那样,库本身可能在程序编译后发生了变化。这就是为什么 ABI 稳定性在平台库中如此重要的原因,因为 ABI 的变化会破坏针对旧版本编译的现有程序。
静态库只是直接从编译器中取出的目标文件包,就像您在项目编译中自己构建的那些文件一样,因此它们以完全相同的方式被拉入并馈送到链接器,而未使用的位是以完全相同的方式下降。