📜  使用共享库 |设置 1

📅  最后修改于: 2022-05-13 01:56:11.630000             🧑  作者: Mango

使用共享库 |设置 1

这篇文章不适合那些算法极客。如果您对系统相关的东西感兴趣,请继续阅读……

共享库可用于共享在许多应用程序中通用的代码。例如,将所有与 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 等)只是为了程序员的方便。它们没有任何意义。所有这些都是二进制文件。您可以随意命名它们。但请确保您在构建应用程序时提供绝对路径。

通常,当我们编译应用程序时,步骤很简单。编译、链接和加载。然而,这并不简单。这些步骤在现代操作系统上更加通用。



当您将应用程序链接到静态库时,代码就是应用程序的一部分。没有依赖性。即使它导致应用程序大小增加,它也有其自身的优势。主要是速度,因为在运行时没有符号(程序实体)解析。由于二进制图像的每一段代码都是一部分,因此此类应用程序与版本不匹配问题无关。但是,成本在于修复库代码中的问题。如果库代码中存在任何错误,则需要重新编译整个应用程序并将其交付给客户端。对于动态库,修复或升级库很容易。您只需要发送更新的共享库。应用程序不需要重新编译,它只需要重新运行。您可以设计一种我们不需要重新启动应用程序的机制。

当我们将应用程序链接到共享库时,链接器会在应用程序加载时留下一些要填充的存根(未解析的符号)。这些存根需要在运行时或应用程序加载时由称为动态链接器的工具填充。再次加载库有两种类型,静态加载和动态加载。不要混淆静态加载静态链接以及动态加载动态链接

例如,您构建了一个依赖于libstdc++.so的应用程序,它是一个共享对象(动态库)。应用程序如何知道所需的共享库? (如果您有兴趣,可以探索 Borland 工具集中的工具 tdump 、objdumpnm或 Linux 上的readelf 工具)。

静态负载:

  • 在静态加载中,所有这些依赖共享库甚至在应用程序开始执行之前就被加载到内存中。如果加载任何共享库失败,应用程序将不会运行。
  • 动态加载器检查应用程序对共享库的依赖性。如果这些库已加载到内存中,则库地址空间会映射到应用程序虚拟地址空间 (VAS),并且动态链接器会对未解析的符号进行重定位。
  • 如果这些库没有加载到内存中(也许您的应用程序可能首先调用共享库),加载器会在标准库路径中搜索并将它们加载到内存中,然后映射和解析符号。再次加载是一个大过程,如果您有兴趣编写自己的加载器:)。
  • 在解析符号时,如果动态链接器找不到任何符号(可能是由于共享库版本较旧),则无法启动应用程序。

动态加载:

  • 顾名思义,动态加载就是按需加载库。
  • 例如,如果您想要共享库中的一个小功能。为什么要在应用程序加载时加载并放在内存中?当您需要这些共享库的功能时,您可以动态调用它们的加载。这称为动态加载。在这种情况下,程序员知道“什么时候应该加载库”的情况。工具集和相关内核提供 API 以支持动态加载和查询共享库中的符号。

更多细节在后面的文章中。

注意:如果您遇到诸如可加载模块或等效术语之类的术语,请不要将它们与共享库混合使用。它们不同于共享库内核提供支持可加载模块的框架。  

使用共享库 | 2套

锻炼:

1. 假设您已经理解了这些概念,您如何设计一个无需重新运行应用程序即可升级到新共享库的应用程序(例如银行业务)。