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

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

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

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

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

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

提前感谢大家的协助。

更新


在向我提供这些术语的上下文中,这实际上是一个必须学习Linux的Windows开发人员团队使用的错误术语。我试图纠正他们,但(不正确的)语言规范仍然存在。

Shared Object: A library that is automatically linked into a program when the program starts, and exists as a standalone file. The library is included in the linking list at compile time (ie: LDOPTS+=-lmylib for a library file named mylib.so). The library must be present at compile time, and when the application starts. Static Library: A library that is merged into the actual program itself at build time for a single (larger) application containing the application code and the library code that is automatically linked into a program when the program is built, and the final binary containing both the main program and the library itself exists as a single standalone binary file. The library is included in the linking list at compile time (ie: LDOPTS+=-lmylib for a library file named mylib.a). The library must be present at compile time. DLL: Essentially the same as a shared object, but rather than being included in the linking list at compile time, the library is loaded via dlopen()/dlsym() commands so that the library does not need to be present at build time for the program to compile. Also, the library does not need to be present (necessarily) at application startup or compile time, as it is only needed at the moment the dlopen/dlsym calls are made. Shared Archive: Essentially the same as a static library, but is compiled with the "export-shared" and "-fPIC" flags. The library is included in the linking list at compile time (ie: LDOPTS+=-lmylibS for a library file named mylibS.a). The distinction between the two is that this additional flag is required if a shared object or DLL wants to statically link the shared archive into its own code AND be able to make the functions in the shared object available to other programs, rather than just using them internal to the DLL. This is useful in the case when someone provides you with a static library, and you wish to repackage it as an SO. The library must be present at compile time.

额外的更新

“DLL”和“共享库”之间的区别只是我当时工作的公司(Windows开发人员被迫转向Linux开发,这个术语被保留下来)的一种(懒惰的,不准确的)口语化说法,遵循上面提到的描述。

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


当前回答

我一直认为dll和共享对象只是同一事物的不同术语——Windows将它们称为dll,而在UNIX系统中,它们是共享对象,通用术语-动态链接库-涵盖了两者(甚至在UNIX中打开一个.so的函数在'动态库'之后被称为dlopen())。

They are indeed only linked at application startup, however your notion of verification against the header file is incorrect. The header file defines prototypes which are required in order to compile the code which uses the library, but at link time the linker looks inside the library itself to make sure the functions it needs are actually there. The linker has to find the function bodies somewhere at link time or it'll raise an error. It ALSO does that at runtime, because as you rightly point out the library itself might have changed since the program was compiled. This is why ABI stability is so important in platform libraries, as the ABI changing is what breaks existing programs compiled against older versions.

静态库只是直接从编译器中取出的目标文件束,就像你自己编译项目的一部分一样,所以它们以完全相同的方式被拉入并提供给链接器,未使用的位以完全相同的方式被删除。

其他回答

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

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

windows上的动态链接库(.dll)类似于linux上的共享库(.so),但与操作系统(windows vs linux)相关的两种实现之间存在一些差异:

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

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

我可以详细介绍Windows中dll的细节,以帮助我在* nix领域的朋友们澄清这些谜团……

DLL类似于共享对象文件。两者都是映像,可以由各自操作系统的程序加载器加载到内存中。这些图像伴随着各种元数据,以帮助链接器和加载器进行必要的关联并使用代码库。

Windows dll有一个导出表。导出可以按名称,也可以按表位置(数字)。后一种方法被认为是“老派的”,而且更加脆弱——重建DLL和改变函数在表中的位置将以灾难告终,而如果通过名称链接入口点,则没有真正的问题。所以,忘记这个问题吧,但如果您使用“恐龙”代码(如第三方供应商库),请注意它的存在。

Windows DLL是通过编译和链接构建的,就像EXE(可执行应用程序)一样,但DLL不是独立的,就像SO是由应用程序使用的一样,要么通过动态加载,要么通过链接时绑定(对SO的引用嵌入在应用程序二进制的元数据中,操作系统程序加载器将自动加载引用的SO)。dll可以引用其他dll,就像SOs可以引用其他SOs一样。

In Windows, DLLs will make available only specific entry points. These are called "exports". The developer can either use a special compiler keyword to make a symbol an externally-visible (to other linkers and the dynamic loader), or the exports can be listed in a module-definition file which is used at link time when the DLL itself is being created. The modern practice is to decorate the function definition with the keyword to export the symbol name. It is also possible to create header files with keywords which will declare that symbol as one to be imported from a DLL outside the current compilation unit. Look up the keywords __declspec(dllexport) and __declspec(dllimport) for more information.

dll的一个有趣的特性是,它们可以声明一个标准的“加载/卸载时”处理函数。无论何时加载或卸载DLL, DLL都可以根据具体情况执行一些初始化或清理。这很好地映射到将DLL作为面向对象的资源管理器,例如设备驱动程序或共享对象接口。

When a developer wants to use an already-built DLL, she must either reference an "export library" (*.LIB) created by the DLL developer when she created the DLL, or she must explicitly load the DLL at run time and request the entry point address by name via the LoadLibrary() and GetProcAddress() mechanisms. Most of the time, linking against a LIB file (which simply contains the linker metadata for the DLL's exported entry points) is the way DLLs get used. Dynamic loading is reserved typically for implementing "polymorphism" or "runtime configurability" in program behaviors (accessing add-ons or later-defined functionality, aka "plugins").

Windows的做事方式有时会引起一些混乱;系统使用. lib扩展名来引用两个普通的静态库(如POSIX *。a文件)和在链接时将应用程序绑定到DLL所需的“导出存根”库。所以,我们应该总是看a *。LIB文件有一个同名*.DLL文件;如果没有,很有可能*。LIB文件是一个静态库存档,并不能导出绑定元数据为DLL。

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

dlopen调用不仅适用于共享对象,如果应用程序希望在运行时代表它这样做,否则共享对象将在应用程序启动时自动加载。dll和。so是一样的东西。dlopen的存在是为了为进程添加更细粒度的动态加载功能。你不需要自己使用dlopen来打开/使用dll,这在应用程序启动时也会发生。

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

.h,以及就此而言,.c/.cpp文件只在包括预处理的编译阶段涉及。一旦创建了目标代码,头文件在链接器开始处理之前就已经很好地完成了。

我一直认为dll和共享对象只是同一事物的不同术语——Windows将它们称为dll,而在UNIX系统中,它们是共享对象,通用术语-动态链接库-涵盖了两者(甚至在UNIX中打开一个.so的函数在'动态库'之后被称为dlopen())。

They are indeed only linked at application startup, however your notion of verification against the header file is incorrect. The header file defines prototypes which are required in order to compile the code which uses the library, but at link time the linker looks inside the library itself to make sure the functions it needs are actually there. The linker has to find the function bodies somewhere at link time or it'll raise an error. It ALSO does that at runtime, because as you rightly point out the library itself might have changed since the program was compiled. This is why ABI stability is so important in platform libraries, as the ABI changing is what breaks existing programs compiled against older versions.

静态库只是直接从编译器中取出的目标文件束,就像你自己编译项目的一部分一样,所以它们以完全相同的方式被拉入并提供给链接器,未使用的位以完全相同的方式被删除。