这篇文章不适合那些算法极客。如果你对系统相关的东西感兴趣,请继续阅读…
共享库在共享代码方面很有用,这在许多应用程序中很常见。例如,将所有与TCP/IP实现相关的代码打包到一个共享库中更经济。然而,数据不能共享,因为每个应用程序都需要自己的数据集。浏览器、ftp、telnet等应用程序利用共享的“网络”库提升特定功能。
每个操作系统都有自己的表示和工具集来创建共享库。这些概念大致相同。在Windows上,每个对象文件(*.obj、*.dll、*.ocx、*.sys、*.exe等)都遵循称为可移植可执行文件的格式。甚至共享库(简称动态链接库或DLL)也以PE格式表示。用于创建这些库的工具集需要理解二进制格式。Linux变体遵循一种称为可执行和可链接格式(ELF)的格式。ELF文件为位置独立(PIC)格式。Linux中的共享库称为共享对象(通常带有扩展名*.so)。这些类似于Windows平台中的DLL。甚至共享对象文件也遵循ELF二进制格式。
记住,文件扩展名(*.dll、*.so、*.a、*.lib等)只是为了方便程序员。它们没有任何意义。所有这些都是二进制文件。你可以随意命名。但要确保在构建应用程序时提供绝对路径。
一般来说,编译应用程序的步骤很简单。编译、链接和加载。然而,这并不简单。这些步骤在现代操作系统上更加通用。
当您将应用程序链接到静态库时,代码是应用程序的一部分。没有依赖性。尽管它会增加应用程序的大小,但它也有自己的优势。最主要的是速度,因为在运行时没有符号(程序实体)解析。由于每段代码都是二进制图像的一部分,因此此类应用程序与版本不匹配问题无关。然而,在库中,代码是一个固定成本的问题。如果库代码中有任何错误,则需要重新编译整个应用程序并将其发送到客户端。对于动态库,修复或升级库很容易。你只需要发送更新的共享库。应用程序不需要重新编译,只需要重新运行。您可以设计一种机制,使我们不需要重新启动应用程序。
当我们将应用程序链接到共享库时,链接器会在应用程序加载时留下一些存根(未解析的符号)。这些存根需要用一种叫做, 动态链接器 在运行时或应用程序加载时。同样,库的加载有两种类型,静态加载和动态加载。不要混淆 静载荷 vs 静态链接 和 动载荷 vs 动态链接 .
例如,您构建了一个依赖于 libstdc++。所以 这是一个共享对象(动态库)。应用程序如何知道所需的共享库?(如果您感兴趣,请浏览这些工具。) tdump 从Borland工具集中, 反汇编 或 纳米 或 雷德尔夫 Linux上的工具)。
静载荷:
- 在静态加载中,即使在应用程序开始执行之前,所有这些依赖的共享库都会加载到内存中。如果加载任何共享库失败,应用程序将无法运行。
- 动态加载程序检查应用程序对共享库的依赖性。如果这些库已经加载到内存中,库地址空间将映射到应用程序虚拟地址空间(VAS),动态链接器将重新定位未解析的符号。
- 如果这些库没有加载到内存中(也许您的应用程序可能是第一个调用共享库的应用程序),加载程序将在标准库路径中搜索并将它们加载到内存中,然后映射和解析符号。同样,加载是一个大过程,如果您有兴趣,请编写自己的加载程序:)。
- 解析符号时,如果动态链接器找不到任何符号(可能是因为旧版本的共享库),则无法启动应用程序。
动态载荷:
- 顾名思义,动态加载是指按需加载库。
- 例如,如果您想要共享库中的一个小功能。为什么它应该在应用程序加载时加载并保存在内存中?当需要这些共享库的功能时,可以动态调用它们的加载。这叫做动态加载。在这种情况下,程序员知道“应该何时加载库”的情况。该工具集和相关内核提供API,以支持动态加载和查询共享库中的符号。
更多细节将在后面的文章中介绍。
注意:如果遇到诸如可加载模块或等效术语之类的术语,不要将它们与共享库混用。它们不同于共享库,内核提供了支持可加载模块的框架。
练习:
1.假设您已经理解了这些概念,您如何设计一个应用程序(例如银行业务),它可以升级到新的共享库,而无需重新运行应用程序。
— 文基 。如果您发现任何不正确的地方,或者您想分享有关上述主题的更多信息,请发表评论。