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

Clo*_*oud 255 c c++ linux dll linker

我参与了关于Linux中的库的一些争论,并想确认一些事情.

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

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

静态库的明显优势是他们让整个应用程序是自包含的,而动态库的好处是"所以"文件可以被替换(即:如果它需要更新由于安全bug),无需重新编译基本应用程序.

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

最后,我还听到一些开发人员提到"共享存档",根据我的理解,这些存档也是静态库本身,但直接由应用程序使用.相反,其他静态库将链接到"共享存档",以将一些(但不是全部)功能/资源从共享存档中提取到正在构建的静态库中.

提前谢谢大家的帮助.

更新


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

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

其他更新

" DLL"和" shared library" 之间的区别只是我当时工作的公司(懒惰,不准确)的口语主义(Windows开发人员被迫转向Linux开发,术语卡住),坚持上述说明.

另外,在S"共享档案"的情况下,库名后面的尾随" "字面只是该公司使用的惯例,而不是一般的行业.

ale*_*oot 183

静态库(.a)中是可直接连接到由接头产生的最终可执行库,它包含在它并没有必要有库入其中可执行将要部署的系统.

共享库(.所以)是链接而不是嵌入在最终的可执行库,因此,当所述可执行启动和需要存在在将可执行部署该系统将被加载.

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

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

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

  • 所有符号在SO库中都不可用.隐藏符号是可能的并且是推荐的,因为库用户没有充分的理由看到您的所有符号. (21认同)
  • 仅供参考:g++ 有`__attribute__` 语法用于选择性地“导出”符号:`#define DLLEXPORT __attribute__ ((visibility("default")))` `#define DLLLOCAL __attribute__ ((visibility("hidden")))` (5认同)
  • +1很好的简单解释。如果一个函数在 DLL 中被声明为“内部”,是否意味着它*不能*从库外部调用? (3认同)

Mat*_*ton 89

我一直认为,DLL和共享对象是同一事物的只是不同的术语- Windows调用DLL的他们,而在UNIX系统上他们共享对象,与一般的名词-动态链接库-涵盖(甚至功能在UNIX上打开一个.so是dlopen()在'动态库'之后调用的.

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

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

  • 那些不这样做的人获得了由进程加载器传递给他们的函数,即linux的elf加载器.如果应用程序想要打开并使用在编译时不存在的.so或.dll,或者只是添加额外的功能(如插件),则存在dlopen. (8认同)
  • 为什么我在 Linux 上看到的一些项目必须使用 dlopen() 调用来访问“.so”文件中的函数,而有些项目根本不需要这样做?顺便说一句,谢谢你! (3认同)
  • 关于 dlopen() 及其函数系列,我的理解是,它用于以编程方式打开/关闭 dll,以便在应用程序的整个运行过程中不必将其加载到内存中。否则,您必须在其命令行参数(也称为 makefile)中告诉链接器您希望加载该库。它将在运行时加载并保持加载在内存中直到应用程序退出。在操作系统级别可能会发生更多事情,但这大致是您的应用程序所关注的情况。 (3认同)
  • 我相信这取决于你如何使用 .so 中的函数,但在这里我对此的了解停止了:/好问题。 (2认同)

JoG*_*sto 32

我可以详细说明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的导出绑定元数据.


rap*_*ura 7

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

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

  • 我相信 dlopen 用于插件或类似功能。权限/限制应该与自动加载相同,无论如何 dlopen 都会递归加载依赖库。 (2认同)