
图片由 Paul Lewin.由Opensource.com修改。CC BY-SA 2.0
当你使用C编程语言编写一个应用程序时,你的代码通常有多个源文件。
最终,这些文件必须被编译成一个可执行文件。你可以通过创建静态库或动态库(后者也被称为共享库)来做到这一点。这两种类型的库在创建和链接的方式上有所不同。两者都有优点和缺点,取决于你的使用情况。
动态链接是最常见的方法,特别是在Linux系统上。动态链接使库保持模块化,因此只要一个库就可以在任何数量的应用程序之间共享。模块化还允许共享库的更新独立于依赖它的应用程序。
在这篇文章中,我演示了动态链接的工作原理。在未来的文章中,我将演示静态链接。
链接器
链接器是一个命令,它将一个程序的几个部分结合在一起,并为它们重新组织内存分配。
链接器的功能包括。
- 整合一个程序的所有部分
- 计算出一个新的内存组织,使所有的部分都适合在一起
- 恢复地址,使程序能够在新的内存组织下运行
- 解决符号性引用
作为所有这些链接器功能的结果,一个可运行的程序被创建,称为可执行程序。在你创建一个动态链接的可执行程序之前,你需要一些库来链接和一个应用程序来编译。准备好你最喜欢的文本编辑器,然后跟着做吧。
创建对象文件
首先,用这些函数签名创建头文件mymath.h 。
int add(int a, int b);
int sub(int a, int b);
int mult(int a, int b);
int divi(int a, int b);
用这些函数定义创建add.c,sub.c,mult.c和divi.c 。我把所有的代码放在一个代码块中,所以把它分成四个文件,如注释中所示。
// add.c
int add(int a, int b){
return (a+b);
}
//sub.c
int sub(int a, int b){
return (a-b);
}
//mult.c
int mult(int a, int b){
return (a*b);
}
//divi.c
int divi(int a, int b){
return (a/b);
}
现在用GCC生成对象文件add.o,sub.o,mult.o, 和divi.o 。
$ gcc -c add.c sub.c mult.c divi.c
-c 选项跳过了链接步骤,只创建对象文件。
创建一个共享对象文件
动态库在最终可执行文件的执行过程中被链接。只有动态库的名字被放在最终的可执行文件中。实际的链接发生在运行时,当可执行文件和库都被放在主内存中时。
除了可共享之外,动态库的另一个优点是它减少了最终可执行文件的大小。使用库的应用程序在创建最终可执行文件时,只包括库的名称,而不是有一个多余的库的副本。
你可以从你现有的样本代码中创建动态库。
$ gcc -Wall -fPIC -c add.c sub.c mult.c divi.c
选项-fPIC 告诉GCC生成与位置无关的代码(PIC)。-Wall 这个选项不是必须的,而且与代码的编译方式无关。但是,它仍然是一个有价值的选项,因为它可以使编译器发出警告,这在排除故障时很有帮助。
使用GCC,创建共享库libmymath.so 。
$ gcc -shared -o libmymath.so \
add.o sub.o mult.o divi.o
现在你已经创建了一个简单的数学库实例,libmymath.so ,你可以在C代码中使用它。当然,也有非常复杂的C语言库,这就是他们的开发者用来生成最终产品的过程,你或我安装在C代码中使用。
接下来,你可以在一些自定义代码中使用你的新数学库,然后将其链接。
创建一个动态链接的可执行文件
假设你写了一个数学的命令。创建一个名为mathDemo.c 的文件,并将这段代码粘贴到其中。
#include <mymath.h>
#include <stdio.h>
#include <stdlib.h>
int main()
{
int x, y;
printf("Enter two numbers\n");
scanf("%d%d",&x,&y);
printf("\n%d + %d = %d", x, y, add(x, y));
printf("\n%d - %d = %d", x, y, sub(x, y));
printf("\n%d * %d = %d", x, y, mult(x, y));
if(y==0){
printf("\nDenominator is zero so can't perform division\n");
exit(0);
}else{
printf("\n%d / %d = %d\n", x, y, divi(x, y));
return 0;
}
}
注意,第一行是一个include 语句,通过名称引用你自己的libmymath 库。要使用一个共享库,你必须安装它。如果你没有安装你使用的库,那么当你的可执行程序运行并搜索所包含的库时,它将无法找到它。如果你需要在不安装库到已知目录的情况下编译代码,有一些方法可以覆盖默认设置。然而,对于一般的使用,我们希望库存在于已知的位置,所以这就是我在这里所演示的。
将文件libmymath.so 复制到一个标准的系统目录,如/usr/lib64 ,然后运行ldconfig 。ldconfig 命令创建所需的链接,并缓存到标准库目录中找到的最新共享库。
$ sudo cp libmymath.so /usr/lib64/
$ sudo ldconfig
编译应用程序
从你的应用程序源代码创建一个名为mathDemo.o 的对象文件(mathDemo.c)。
$ gcc -I . -c mathDemo.c
-I 选项告诉GCC在它后面列出的目录中搜索头文件(本例中为mymath.h )。在这种情况下,你指定的是当前目录,用一个点来表示 (.)。创建一个可执行文件,用-l 选项来引用你的共享数学库的名称。
$ gcc -o mathDynamic mathDemo.o -lmymath
GCC会找到libmymath.so ,因为它存在于一个默认的系统库目录中。使用ldd 来验证所使用的共享库。
$ ldd mathDemo
linux-vdso.so.1 (0x00007fffe6a30000)
libmymath.so => /usr/lib64/libmymath.so (0x00007fe4d4d33000)
libc.so.6 => /lib64/libc.so.6 (0x00007fe4d4b29000)
/lib64/ld-linux-x86-64.so.2 (0x00007fe4d4d4e000)
看一下mathDemo 可执行文件的大小。
$ du ./mathDynamic
24 ./mathDynamic
当然,这是一个小程序,它所占用的磁盘空间也反映了这一点。作为比较,同样代码的静态链接版本(你将在我的下一篇文章中看到)是932K!你可以验证它是动态的。
$ ./mathDynamic
Enter two numbers
25
5
25 + 5 = 30
25 - 5 = 20
25 * 5 = 125
25 / 5 = 5
你可以用file 命令来验证它是动态链接的。
$ file ./mathDynamic
./mathDynamic: ELF 64-bit LSB executable, x86-64,
dynamically linked,
interpreter /lib64/ld-linux-x86-64.so.2,
with debug_info, not stripped
动态链接
一个共享库导致了一个轻量级的可执行文件,因为链接发生在运行期间。因为它在运行期间解决引用问题,所以它确实需要更多的时间来执行。然而,由于日常Linux系统中的绝大多数命令都是动态链接的,而且是在现代硬件上,所节省的时间可以忽略不计。它固有的模块化对于开发者和用户来说都是一个强大的功能。
在这篇文章中,我描述了如何创建动态库并将其链接到最终的可执行文件中。