Linux静态库和共享库
linux库学习笔记
一、什么是库
本质上来说库是一种可执行的二进制形式代码,可以被操作系统载入内存执行。由于windows和linux的本质不同,因此二者库的二进制是不兼容的,本文主要是针对linux系统下的库进行说明的。
二、库的种类
linux下的库有两种:静态库和共享库(动态库)。二者的不同点在于代码被载入的时刻不同。
• 静态库:在程序编译时会被连接到目标代码中,程序运行时将不再需要该静态库,因此体积较大。
• 共享库:在程序编译时并不会被连接到目标代码中,只是在生成的可执行程序中简单指定需要使用的库函数信息,而是在程序运行是才被载入,因此在程序运行时还需要动态库存在,因此代码体积较小。
三、库文件的命名规则
• 静态库: libxxxx.a,其中xxxx是该lib的名称
• 共享库: libxxxx.so.major.minor,xxxx是该lib的名称,major是主版本号,minor是副版本号
四、库存在的意义
库是别人写好的现有的,成熟的,可以复用的代码,你可以使用但要记得遵守许可协议。
现实中每个程序都要依赖很多基础的底层库,不可能每个人的代码都从零开始,因此库的存在意义非同寻常。
封装
版本管理
共享库的好处是,不同的应用程序如果调用相同的库,那么在内存里只需要有一份该共享库的实例。
五、库的创建与使用
静态库的创建
Step1.由源文件编译生成一堆.o,每个.o里都包含这个编译单元的符号表
Step2.ar命令将很多.o转换成静态库,如
#ar crs libhello.a hello.o
共享库的创建
Step1.由源文件编译生成一堆.o,每个.o里都包含这个编译单元的符号表
Step2. 由编译器加特定参数编译生成共享库,如
# gcc-shared -fPCI -o libhello.so hello.o
库的使用
#gcc-o hello main.c -L. –lhello
五、动态库加载的问题
1. 拷贝动态库文件到/lib或/usr/lib去
2. 改变环境变量LD_LIBRARY_PATH
命令:#export LD_LIBRARY_PATH=/home/xxx/lib/
3. 在/etc/ld.so.conf添加库的路径,然后执行ldconfig命令生效
4. 在编译时指定库的搜索路径,-Wl,-rpath=/home/xxx/lib(应该是库在系统中的绝对路径)
5. 动态加载库
六、动态加载库接口
1. dlopen
函数原型:void*dlopen(constchar *libname,int flag);
功能描述:dlopen必须在dlerror,dlsym和dlclose之前调用,表示要将库装载到内存,准备使用。
如果要装载的库依赖于其它库,必须首先装载依赖库。如果dlopen操作失败,返回NULL值;如果库已经被装载过,则dlopen会返回同样的句柄。
Libname:一般是库的全路径,这样dlopen会直接装载该文件;如果只是指定了库名称,在dlopen会按照下面的机制去搜寻:
a. 根据环境变量LD_LIBRARY_PATH查找
b. 根据/etc/ld.so.cache查找
c. 查找依次在/lib和/usr/lib目录查找。
Flag:表示处理未定义函数的方式,可以使用RTLD_LAZY或RTLD_NOW。
RTLD_LAZY:表示暂时不去处理未定义函数,先把库装载到内存,等用到没定义的函数再说;
RTLD_NOW:表示马上检查是否存在未定义的函数,若存在,则dlopen以失败告终。
2. dlerror
函数原型:char*dlerror(void);
功能描述:dlerror可以获得最近一次dlopen,dlsym或dlclose操作的错误信息,返回NULL表示无错误。dlerror在返回错误信息的同时,也会清除错误信息。
3. dlsym
函数原型:void*dlsym(void*handle,const char *symbol);
功能描述:在dlopen之后,库被装载到内存。dlsym可以获得指定函数(symbol)在内存中的位置(指针)。如果找不到指定函数,则dlsym会返回NULL值。但判断函数是否存在最好的方法是使用dlerror函数,
4. dlclose
函数原型:int dlclose(void*);
功能描述:将已经装载的库句柄减一,如果句柄减至零,则该库会被卸载。如果存在析构函数,则在dlclose之后,析构函数会被调用。
七、与库相关的常用命令
1. nm:用来列出目标文件的符号清单。
2. ldd:显示可执行模块的dependency。
八、实例测试
① 编写源文件
hello.c
#include<stdio.h> void sayhello() { printf("hello world \n"); } |
libtest.c
#include<stdio.h> int main(){ sayhello(); return 0; } |
② 编译生成目标文件(.o)
使用命令:$gcc –c -fPIC hello.c
$gcc –c -fPIC libtest.c
③ 库文件生成与使用
1. 静态库
使用命令:$ar –crs libhelloa.a hello.o
2. 共享库
使用命令:$gcc -fPIC -shared -o libhellos.so hello.o
3. 编译目标文件
使用命令(使用静态库):$ gcc -o libtesta libtest.o -L. –lhelloa
使用命令(使用共享库):$ gcc -L. -lhello -Wl,-rpath=./ -o libtestslibtest.o
④ 結果
$./libtesta
$ helloworld
$./libtests
$ helloworld
使用命令ldd查看依赖,结果如下:
动态加载库实例
① 编写源文件
dlibtest.c
#include<stdio.h> #include<dlfcn.h> int main(int argc,char **argv){ void *handle; int (*func)(void); handle=dlopen("./libhellos.so",RTLD_LAZY); if(!handle){ printf("load lib error\n"); return 1; } func = (int (*)(void))dlsym(handle, "sayhello"); if (func == NULL) { printf("Load symbol myfunction fail, %s\n", dlerror()); return 2; } func(); dlfclose(handle); return 0; } |
② 编译
使用命令:$ gcc-o dlibtest dlibtest.c -ldl
③ 結果
$./dlibtest