如何在 Linux 上动态链接模块库
学习如何用动态链接库将多个 C 目标文件结合到一个单个的可执行文件之中。
当使用 C 编程语言编写一个应用程序时,你的代码通常有多个源文件代码。
最终,这些文件必须被编译到一个单个的可执行文件之中。你可以通过创建静态或动态库(后者也被称为 共享 库)来实现这一点。这两种类型的库在创建和链接的方式上有所不同。两者都有缺点和优点,这取决于你的使用情况。
动态链接是最常见的方法,尤其是在 Linux 系统上。动态链接会保持库模块化,因此,很多应用程序可以共享一个库。应用程序的模块化也允许单独更新其依赖的共享库。
在这篇文章中,我将演示动态链接是如何工作的。在后期的文章中,我将演示静态链接。
链接器
链接器 是一个命令,它将一个程序的数个部分结合在一起,并为它们重新组织内存分配。
链接器的功能包括:
- 整合一个程序的所有的部分
- 计算出一个新的内存组织结构,以便所有的部分组合在一起
- 恢复内存地址,以便程序可以在新的内存组织结构下运行
- 解析符号引用
链接器通过这些功能,创建了一个名为 可执行文件 的可以运行的程序。在你创建一个动态链接的可执行文件前,你需要一些用来链接的库,和一个用来编译的应用程序。准备好你 最喜欢的文本编辑器 并继续。
创建目标文件
首先,创建带有这些函数签名的头文件 mymath.h
:
1 |
|
使用这些函数定义来创建 add.c
、sub.c
、mult.c
和 divi.c
文件。我将把所有的代码都放置到一个代码块中,请将其分为四个文件,如注释所示:
1 |
|
现在,使用 GCC 来创建目标文件 add.o
、sub.o
、mult.o
和 divi.o
:
(LCTT 校注:关于“ 目标文件 ”,有时候也被称作“对象文件”,对此,存在一些译法混乱情形,称之为“目标文件”的译法比较流行,本文采用此译法。)
1 |
|
-c
选项跳过链接步骤,并且只创建目标文件。
创建一个共享的目标文件
在最终的可执行文件的执行过程中将链接动态库。在最终的可执行文件中仅放置动态库的名称。实际上的链接过程发生在运行时,在此期间,可执行文件和库都被放置到了主内存中。
除了可共享外,动态库的另外一个优点是它减少了最终的可执行文件的大小。在一个应用程序最终的可执行文件生成时,其使用的库只包括该库的名称,而不是该库的一个多余的副本。
你可以从你现有的示例代码中创建动态库:
1 |
|
选项 -fPIC
告诉 GCC 来生成 位置无关的代码 (PIC)。-Wall
选项不是必需的,并且与代码的编译方式是无关的。不过,它却是一个有价值的选项,因为它会启用编译器警告,这在排除故障时是很有帮助的。
使用 GCC ,创建共享库 libmymath.so
:
1 |
|
现在,你已经创建了一个简单的示例数学库 libmymath.so
,你可以在 C 代码中使用它。当然,也有非常复杂的 C 库,这就是他们这些开发者来生成最终产品的工艺流程,你和我可以安装这些库并在 C 代码中使用。
接下来,你可以在一些自定义代码中使用你的新数学库,然后链接它。
创建一个动态链接的可执行文件
假设你已经为数学运算编写了一个命令。创建一个名称为 mathDemo.c
的文件,并将这些代码复制粘贴至其中:
1 |
|
注意:第一行是一个 include
语句,通过名称来引用你自己的 libmymath
库。要使用一个共享库,你必须已经安装了它,如果你没有安装你将要使用的库,那么当你的可执行文件在运行并搜索其包含的库时,将找不到该共享库。如果你需要在不安装库到已知目录的情况下编译代码,这里有 一些方法可以覆盖默认设置。不过,对于一般使用来说,我们希望库存在于已知的位置,因此,这就是我在这里演示的东西。
复制文件 libmymath.so
到一个标准的系统目录,例如:/usr/lib64
, 然后运行 ldconfig
。ldconfig
命令创建所需的链接,并缓存到标准库目录中发现的最新共享库。
1 |
|
编译应用程序
从你的应用程序源文件代码(mathDemo.c
)中创建一个名称为 mathDemo.o
的目标文件:
1 |
|
-I
选项告诉 GCC 来在其后所列出的目录中搜索头文件(在这个示例中是 mymath.h
)。在这个示例中,你指定的是当前目录,通过一个单点(.
)来表示。创建一个可执行文件,使用 -l
选项来通过名称来引用你的共享数学库:
1 |
|
GCC 会找到 libmymath.so
,因为它存在于一个默认的系统库目录中。使用 ldd
来查证所使用的共享库:
1 |
|
看看 mathDemo
可执行文件的大小:
1 |
|
当然,它是一个小的应用程序,它所占用的磁盘空间量也反映了这一点。相比之下,相同代码的一个静态链接版本(正如你将在我后期的文章所看到的一样)是 932K !
1 |
|
你可以使用 file
命令来查证它是动态链接的:
1 |
|
成功!
动态链接
因为链接发生在运行时,所以,使用一个共享库会产生一个轻量型的可执行文件。因为它在运行时解析引用,所以它会花费更多的执行时间。不过,因为在日常使用的 Linux 系统上绝大多数的命令是动态链接的,并且在现代硬件上,所能节省的时间是可以忽略不计的。对开发者和用户来说,它的固有模块性是一种强大的功能。
在这篇文章中,我描述了如何创建动态库,并将其链接到一个最终可执行文件。在我的下一篇文章中,我将使用相同的源文件代码来创建一个静态链接的可执行文件。
via: https://opensource.com/article/22/5/dynamic-linking-modular-libraries-linux
作者:Jayashree Huttanagoudar 选题:lkxed 译者:robsean 校对:wxy