在C语言中,函数库分为两种类型,一种是静态库(库程序是直接注入目标程序,不分彼此,库文件通常以.a结尾),另一种为动态库(库程序是在运行目标程序时加载,库文件通常以.so结尾)。
文件到可执行文件的编译过程:
静态库 VS 动态库
静态库特点:
静态库的代码在编译的过程中就已经载入可执行文件中,所以最后生成的可执行文件相对较大。
静态库在链接的时候把库直接加载到程序中
静态库的名字一般是libxxx.a在编译时可以直接编译进可执行文件中,运行环境中可以不存在库文件,但是如果库文件更新了,可执行文件需要重新编译
编译生成静态库:
gcc -c hello.c
ar crv libhello.a hello.o # 将目标文件Hello.o打包成静态库文件
调用静态库:gcc main.c -L . -lhello (-L后面是静态库文件所在目录)
动态库特点:
动态库的代码在可执行文件运行时才载入内存,在编译过程中仅简单的引用,所以最后生成的可执行文件较小。
动态库链接的时候,只是保留接口,将动态库和程序代码独立,这样可以提高代码的可复用度和降低程序的耦合度
动态库的代码是可执行文件在运行中加载执行,也就是说,程序运行环境中要有动态库文件,一般以libxxx.so命令,优点是方便升级,动态库更新,可执行文件不用重新编译
编译生成动态库:
gcc -fPIC -shared -o libhello.so hello
(-fPIC是常见与地址无关的编译程序,为了在多个应用程序间共享, -shared指定生成动态连接库)
调用动态库:
一般在运行环境中直接运行可执行文件,前提动态库文件也在运行环境中,需要注意的是系统在运行程序的时候,需要知道动态库的名称和位置,这样才能加载,如果找不到动态库就会直接程序退出报错。
所以编译程序的时候要使用下面方式编译:
gcc main.c -o main -L ./ -lhello
(-L后面是库文件的路径,最好是绝对路径。-l加上去掉lib的库名。然后执行可执行文件就可以了)
还有一种加载动态库的方式:
linux提供dlopen、dlsym、dlerror和dlclose函数获取动态链接库的函数。通过这四个函数可以实现一个插件程序,方便程序的扩展和维护。格式如下:
#include <dlfcn.h>
// 用dlopen函数打开库文件,并指定打开方式
void *dlopen(const char *filename, int flag);
// 用dlerror()函数测试是否打开成功,并进行错误处理;
char *dlerror(void);
// 用dlsym获得函数地址,存放在一个函数指针中
void *dlsym(void *handle, const char *symbol);
// 程序结束时用dlclose关闭打开的动态库,防止资源泄露
int dlclose(void *handle);
使用这些函数去找对应的库函数入口地址,然后执行。
参考文章:
https://developer.aliyun.com/article/484669
https://zhuanlan.zhihu.com/p/307640255