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

我参与了一些有关Linux的图书馆的辩论,并想确认一些事情。

这是我的理解(请纠正我,如果我错了,我会编辑我的post后),build立一个应用程序时有两种使用库的方式:

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

静态库的明显优势在于它允许整个应用程序是自包含的,而dynamic库的好处是可以replace“.so”文件(即:由于安全性需要更新错误),而不需要重新编译基础应用程序。

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

最后,我也听说一些开发人员提到“共享档案”,据我了解,这也是静态库本身,但从来没有被应用程序直接使用。 相反,其他静态库将链接到“共享归档”,以将共享归档中的一些(但不是全部)function/资源引入正在构build的静态库中。

预先感谢您的帮助。

更新


在向我提供这些条款的背景下,我发现了这些术语之间的细微差别,甚至可能只是我的行业中的俗语:

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

其他更新

DLL ”和“ shared library ”之间的区别只是当时我所在公司的口语,坚持上面的描述。

另外,在“共享档案”的情况下,库名称后面的“ S ”后面的文字只是该公司使用的惯例,而不是一般的行业。

我一直认为DLL和共享对象对于同一个事物来说只是不同的术语–Windows将它们称为DLL,而在UNIX系统中,它们是共享对象,通用术语 – dynamic链接库 – 涵盖了两个方面在UNIX上打开一个.so就是在'dynamic库'之后调用dlopen() )。

它们确实只在应用程序启动时被链接,然而你对头文件的validation概念是不正确的。 头文件定义了为了编译使用库的代码所需要的原型,但是在链接时链接器在库本身内部查找,以确保它所需要的function实际上在那里。 链接器必须在链接时find函数体,否则会引发错误。 在运行时也是如此,因为正确地指出自程序编译以来,库本身可能已经改变了。 这就是ABI稳定性在平台库中如此重要的原因,因为ABI的变化是破坏了针对旧版本编译的现有程序的原因。

静态库就是直接从编译器中取出的对象文件的捆绑包,就像你自己将其作为项目编译的一部分构build的那样,所以它们被拉进来并以完全相同的方式送到链接器,而未使用的位是以完全相同的方式下降。

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

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

Windows(.dll)上dynamic链接库就像Linux上的共享库(.so),但与操作系统(Windows和Linux)相关的两个实现之间存在一些差异:

DLL可以定义两种function:导出和内部。 导出的函数将被其他模块调用,也可以从定义它们的DLL中调用。 内部函数通常只能从定义它们的DLL中调用。

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

我可以详细说明Windows中DLL的细节,以帮助我的朋友们在* NIX-land中澄清这些谜团…

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

Windows DLL有一个导出表。 导出可以是名称,也可以是表格位置(数字)。 后一种方法被认为是“老派”,而且更为脆弱 – 重buildDLL,改变表中某个函数的位置将在灾难中结束,而入口点的链接是按名称链接的则不存在实际问题。 所以,把这个问题当成一个问题来解决,但是要知道,如果你使用“恐龙”代码(比如第三方供应商库)来处理这个问题。

Windows DLL是通过编译和链接构build的,就像您对EXE(可执行应用程序)所做的一样,但是DLL并不是单独存在的,就像SO应该由应用程序使用一样,要么通过dynamic加载,要么通过链接时绑定(对SO的引用embedded到应用程序二进制文件的元数据中,OS程序加载器将自动加载引用的SO)。 DLL可以引用其他DLL,就像SO可以引用其他SO一样。

在Windows中,DLL将只提供特定的入口点。 这些被称为“出口”。 开发人员可以使用特殊的编译器关键字使符号成为外部可见的(对于其他链接器和dynamic装载器),或者可以在链接时使用的模块定义文件中列出导出,当DLL本身是正在创build。 现代的做法是用关键字来装饰函数定义来导出符号名称。 也可以使用关键字创build头文件,这些关键字会将该符号声明为从当前编译单元之外的DLL导入的符号。 查找关键字__declspec(dllexport)和__declspec(dllimport)以获取更多信息。

DLL的一个有趣的特性是它们可以声明一个标准的“加载/卸载”处理函数。 每当DLL被加载或卸载,DLL可以执行一些初始化或清理,视情况而定。 这很好地映射为具有一个DLL作为面向对象的资源pipe理器,如设备驱动程序或共享对象接口。

当开发人员想要使用已经构build的DLL时,她必须在创buildDLL时引用DLL开发人员创build的“导出库”(* .LIB),或者必须在运行时显式加载DLL,并请求通过LoadLibrary()和GetProcAddress()机制按名称input地址。 大多数情况下,链接到一个LIB文件(它只包含DLL的导出入口点的链接程序元数据)是DLL的使用方式。 dynamic加载通常用于在程序行为中实现“多态性”或“运行时可configuration性”(访问加载项或后来定义的function,也称为“插件”)。

Windows的做事方式有时会造成一些混乱, 系统使用.LIB扩展来引用正常的静态库(存档,如POSIX * .a文件),以及在链接时将应用程序绑定到DLL所需的“导出存根”库。 因此,应该总是查看* .LIB文件是否具有相同名称的* .DLL文件; 如果不是,那么* .LIB文件是一个静态库存档,而不是为DLL导出绑定元数据。

你是正确的,静态文件被复制到应用程序在链接时,共享文件只是在链接时validation,并在运行时加载。

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