C语言 共享库(动态库)制作
共享库简介
共享库的本质就是将多个目标文件打包成一个文件。链接共享库就是在可执行文件中调用共享库中函数的指令处插入一些指令,在运行时通过执行这些指令来完成加载共享库以及计算出所调函数入口地址的动作。使用共享库的可执行文件占用磁盘和内存相对于静态库要小。共享库中的代码一旦被修改,只要函数接口不变,无需重新链接。使用共享库的可执行文件在运行时,需要依赖共享库。一旦共享库被删除,则可执行文件无法运行。
共享库的文件形式为:lib<库名>.so
共享库制作
代码实现
假如我们制定一个数学操作的共享库,在同一个文件夹下,有如下文件:
– add.c
– add.h
– sub.c
– sub.h
add.c为加法操作实现,sub.c为减法操作实现。
代码如下:
add.h
#ifndef C_ADD_H
#define C_ADD_H
int add(int a, int b);
#endif //C_ADD_H
add.c
#include "add.h"
int add(int a, int b) {
return a + b;
}
sub.h
#ifndef C_SUB_H
#define C_SUB_H
int sub(int a, int b);
#endif //C_SUB_H
sub.c
#include "sub.h"
int sub(int a, int b) {
return a - b;
}
创建共享库
首先我们将源代码编译为目标文件:
gcc命令需要加-fPIC
选项,将目标文件编译为位置无关码,动态库内部跳转都是通过全局映射表来将程序中的虚拟地址找到动态库的实际地址,来进行调用的。gcc -fPIC -c add.c sub.c
执行完毕后会生产add.o和sub.o文件。
然后使用gcc的-shared
选项来表明创建一个共享库:gcc -shared -o libmymath.so sub.o add.o
执行完后,会创建一个libmymath.so文件。
链接使用共享库
我们为了使用上面的共享库,我们在如上相同文件夹下,创建一个main.c文件:
– add.c # 加法实现
– add.h # 加法头文件
– sub.c # 减法实现
– sub.h # 减法头文件
– libmymath.so # 共享库文件
– main.c # 入口主程序
main.c
#include <stdio.h>
#include "add.h"
#include "sub.h"
int main(void) {
int a = 20;
int b = 10;
printf("%d+%d=%d\n", a, b, add(a, b));
printf("%d-%d=%d\n", a, b, sub(a, b));
return 0;
}
我们使用#include预处理指令包含了sub.h和add.h头文件,来使用我们制作的mymath共享库。
如果我们直接gcc -o main main.c
肯定会出错,因为编译器不知道add.h和sub.h是哪里来的,也不知道add和sub函数的定义在哪里。
所以我们需要使用-l
选项,让编译器链接到我们制作的共享库:gcc -o main -lmymath main.c
执行上面的命令发现还是会报错,是因为编译器默认查找的/usr/lib;/usr/local/lib系统文件夹中的库。而非我们的制作的共享库存放地址,除非将你制作的libmymath.a放到上面的lib文件夹中,那么执行上面的命令就不会有问题。如果不在上述文件夹中,则需要使用-L
来指定附加库文件搜索路径。
此处我的libmymath.a在当前目录,所以执行:gcc -o main -lmymath -L./ main.c
注意:gcc的
-l
选项后面跟的库名,只需要指定除去lib和.so后缀的中间的实际命名就可以了,编译器会自动按照lib<库名>.so
规则来搜索库文件。
如果预处理指令#include包含的头文件不在当前目录下,则需要在gcc的时候,通过-I
选项来指定头文件附加搜索路径。
此时在当前目录生成了main的可执行文件。
运行得到如下:
20+10=30
20-10=10
可执行文件在运行期需加载动态库文件,所以如果动态库被删除,那么执行文件会报错。
至此,从动态库的制作到链接使用就结束了。
小结
创建动态库gcc编译目标文件需使用-fpic
选项来为目标文件生成位置无关码:gcc -fPIC -c xx1.c xx2.c
将多个目标文件打包成一个libxx.so文件:gcc -shared -o libxx.so xx1.o xx2.o
在可执行程序编译链接共享库,和链接静态库是一样的:gcc -l<库名> -L<库附加搜索路径> xx.c