库的分类
根据链接时期的不同,库分为静态库和动态库之分。
静态库:在链接阶段被链接的,生成的可执行文件就不受库的影响了,即使库被删除了,程序依然可以成功运行。
动态库:在程序执行的时候被链接的,即使程序编译完,库仍须保留在系统上,以供程序运行时调用。
动态链接库
一、隐式调用
--> 创建动态链接库
#include<stdio.h> void hello() { printf("hello world/n"); }
编译生成动态库libhello.so:
gcc -fPIC -shared hello.c -o libhello.so
-shared 该选项指定生成动态连接库(让连接器生成T类型的导出符号表,有时候也生成弱连接W类型的导出符号),不用该标志外部程序无法连接。相当于一个可执行文件;
-fpic 选项表示编译为位置独立的代码,不用此选项的话编译后的代码是位置相关的所以动态载入时是通过代码拷贝的方式来满足不同进程的需要,而不能达到真正代码段共享的目的。
--> 再编辑一个测试文件test.c,内容如下
#include<stdio.h> int main() { printf("call hello()"); hello(); }
编译:
gcc test.c -lhello -L. -o test
-l 选项告诉编译器要使用hello这个库。奇怪的地方是动态库的名字是libhello.so,这里却使用hello;-L 选项告诉编译器在当前目录中查找库文件。
--> 编译成功后执行./test, 仍然出错,说找不到库
有两种方法:
1、可以把当前路径加入 /etc/ld.so.conf中然后运行ldconfig,或者以当前路径为参数运行ldconfig(要有root权限才行)。
2、把当前路径加入环境变量LD_LIBRARY_PATH中, 如:export LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH
二、显式调用
显式调用需要包含头文件#include <dlfcn.h>。涉及到下面几个函数:dlopen()、dlsym()、dlerror()、dlclose()。
dlopen() // 函数以指定模式打开指定的动态链接库文件,并返回一个句柄给dlsym()的调用进程。 dlcolse() // 用来卸载打开的库。 dlerror() // 当动态链接库操作函数执行失败时,可以返回出错信息,返回值为NULL时表示操作函数执行成功。
编译时候要加入 -ldl (指定dl库)
具体的函数原型如下:
void *dlopen(const char *filename, int flag); char *dlerror(void); void *dlsym(void *handle, const char *symbol); int dlclose(void *handle);
调用示例:
#include <stdio.h> #include <stdlib.h> include <dlfcn.h> //动态链接库路径 #define LIB_CACULATE_PATH "./libcaculate.so" //函数指针 typedef int (*CAC_FUNC)(int, int); int main() { void *handle; char *error; CAC_FUNC cac_func = NULL; //打开动态链接库 handle = dlopen(LIB_CACULATE_PATH, RTLD_LAZY); if (!handle) { fprintf(stderr, "%s ", dlerror()); exit(EXIT_FAILURE); } //清除之前存在的错误 dlerror(); //获取一个函数 *(void **) (&cac_func) = dlsym(handle, "add"); if ((error = dlerror()) != NULL) { fprintf(stderr, "%s ", error); exit(EXIT_FAILURE); } printf("add: %d ", (*cac_func)(2,7)); cac_func = (CAC_FUNC)dlsym(handle, "sub"); printf("sub: %d ", cac_func(9,2)); cac_func = (CAC_FUNC)dlsym(handle, "mul"); printf("mul: %d ", cac_func(3,2)); cac_func = (CAC_FUNC)dlsym(handle, "div"); printf("div: %d ", cac_func(8,2)); //关闭动态链接库 dlclose(handle); exit(EXIT_SUCCESS); }
静态链接库
仍使用刚才的hello.c和test.c。
1. gcc -c hello.c 注意这里没有使用-shared选项
2. 把目标文件归档 :
ar -r libhello.a hello.o
程序 ar 配合参数 -r 创建一个新库 libhello.a 并将命令行中列出的对象文件插入。采用这种方法,如果库不存在的话,参数 -r 将创建一个新的库,而如果库存在的话,将用新的模块替换原来的模块。
3. 在程序中链接静态库:
gcc test.c -lhello -L. -static -o hello.static // 或者 gcc test.c libhello.a -L. -o hello.static
生成的hello.static就不再依赖libhello.a了LD_LIBRARY_PATH
静态库和动态库的比较
静态库被链接后库直接嵌入到可执行文件中,缺点:
1、造成系统空间被浪费。如果多个程序链接了同一个库,则每一个生成的可执行文件就都会有一个库的副本,必然会浪费系统空间。
2、一旦发现了库中有bug,挽救起来就比较麻烦。必须一一把链接该库的程序找出来,然后重新编译。
动态库是在程序运行时被链接的,弥补上述不足:
1、磁盘上只须保留一份副本,因此节约了磁盘空间。
2、如果发现了bug或要升级也很简单,只要用新的库把原来的替换掉就行了。