繁体   English   中英

共享对象 (.so)、静态库 (.a) 和 DLL (.so) 之间的区别?

[英]Difference between shared objects (.so), static libraries (.a), and DLL's (.so)?

我参与了一些关于 Linux 库的辩论,并想确认一些事情。

据我了解(如果我错了,请纠正我,稍后我会编辑我的帖子),在构建应用程序时有两种使用库的方法:

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

    静态库的明显优势是它们允许整个应用程序是自包含的,而动态库的好处是可以替换“.so”文件(即:如果由于安全原因需要更新它bug) 无需重新编译基础应用程序。

    我听说有些人区分共享对象和动态链接库 (DLL),即使它们都是“.so”文件。 在 Linux 或任何其他符合 POSIX 的操作系统(即:MINIX、UNIX、QNX 等)上进行 C\/C++ 开发时,共享对象和 DLL 之间有什么区别吗? 有人告诉我,一个关键的区别(到目前为止)是共享对象只在运行时使用,而 DLL 必须首先使用应用程序中的 dlopen() 调用打开。

    最后,我还听到一些开发人员提到“共享档案”,据我了解,它本身也是静态库,但从未被应用程序直接使用。 相反,其他静态库将链接到“共享档案”,以将一些(但不是全部)功能\/资源从共享档案中提取到正在构建的静态库中。

    预先感谢大家的帮助。

    更新<\/h1>

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

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

      额外更新<\/h1>

      DLL<\/code> ”和“ shared library<\/code> ”之间的区别只是我当时工作的公司的一种(懒惰、不准确的)俗语(Windows 开发人员被迫转向 Linux 开发,并且这个术语卡住了),坚持描述如上所述。

      此外,在“共享档案”的情况下,库名称后面的尾随“ S<\/code> ”文字只是该公司使用的惯例,而不是一般行业中的惯例。

静态库(.a)是一个可以直接链接到链接器生成的最终可执行文件的库,它包含在其中,并且不需要将库放入将部署可执行文件的系统中。

共享库(.so)是一个链接但未嵌入最终可执行文件的库,因此将在可执行文件启动时加载,并且需要在部署可执行文件的系统中存在。

Windows(.dll)上动态链接库就像linux上的共享库(.so),但是与OS相关的两个实现之间存在一些差异(Windows vs Linux):

DLL可以定义两种功能:导出和内部。 导出的函数旨在由其他模块调用,也可以在定义它们的DLL中调用。 内部函数通常仅用于在定义它们的DLL中调用。

Linux上的SO库不需要特殊的导出语句来指示可导出的符号,因为所有符号都可用于询问过程。

我一直认为DLL和共享对象只是同一个东西的不同术语--Windows称之为DLL,而在UNIX系统上它们是共享对象,通用术语 - 动态链接库 - 覆盖两者(甚至是在UNIX上打开一个.so在'动态库'之后被称为dlopen() )。

它们确实只在应用程序启动时链接,但是您对头文件的验证概念不正确。 头文件定义了编译使用库的代码所需的原型,但是在链接时链接器查看库本身以确保它所需的功能实际存在。 链接器必须在链接时找到函数体,否则会引发错误。 它也可以在运行时执行此操作,因为正如您正确指出的那样,自编译程序以来,库本身可能已经发生了变化。 这就是为什么ABI稳定性在平台库中如此重要的原因,因为ABI的变化是破坏针对旧版本编译的现有程序的原因。

静态库只是直接来自编译器的目标文件包,就像你在项目编译过程中自己构建的一样,所以它们以完全相同的方式被拉入并提供给链接器,未使用的位是以完全相同的方式掉落。

我可以详细说明Windows中DLL的细节,以帮助澄清这些神秘事物给我在这里的朋友* NIX-land ...

DLL就像一个共享对象文件。 两者都是图像,准备通过相应OS的程序加载器加载到存储器中。 图像伴随着各种元数据,以帮助链接器和加载器进行必要的关联并使用代码库。

Windows DLL具有导出表。 导出可以是名称,也可以是表位(数字)。 后一种方法被认为是“老派”并且更加脆弱 - 重建DLL并改变表中函数的位置将以灾难告终,而如果按名称链接入口点则没有实际问题。 所以,忘记这是一个问题,但只要知道它就在那里,如果你使用“恐龙”代码,如第三方供应商的库。

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的导出绑定元数据。

您是正确的,因为静态文件在链接时被复制到应用程序,并且该共享文件仅在链接时验证并在运行时加载。

dlopen调用不仅适用于共享对象,如果应用程序希望在运行时代表它,则在应用程序启动时自动加载共享对象。 DLLS和.so是一回事。 dlopen的存在是为了为进程添加更细粒度的动态加载能力。 您不必自己使用dlopen打开/使用DLL,这在应用程序启动时也会发生。

我怀疑这里存在某种误解,但头文件,至少是用于编译源代码的 .h 文件,绝对不会在链接时检查。

.h 和 .c/.cpp 文件仅在编译阶段涉及,其中包括预处理。 一旦创建了目标代码,在链接器开始处理事情之前,头文件早就消失了。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM