静态库与共享库分析
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
静态库与共享库分析
Linux静态库和共享库
⼀、什么是库
本质上来说库是⼀种可执⾏的⼆进制形式代码,可以被操作系统载⼊内存执⾏。
由于windows 和linux的本质不同,因此⼆者库的⼆进制是不兼容的,本⽂主要是针对linux系统下的库进⾏说明的。
⼆、库的种类
linux下的库有两种:静态库和共享库(动态库)。
⼆者的不同点在于代码被载⼊的时刻不同。
静态库:在程序编译时会被连接到⽬标代码中,程序运⾏时将不再需要该静态库,因此体积较⼤。
共享库:在程序编译时并不会被连接到⽬标代码中,只是在⽣成的可执⾏程序中简单指定需要使⽤的库函数信息,⽽是在程序运⾏是才被载⼊,因此在程序运⾏时还
需要动态库存在,因此代码体积较⼩。
三、库⽂件的命名规则
静态库: libxxxx.a,其中xxxx是该lib的名称
共享库: libxxxx.so.major.minor,xxxx是该lib的名称,major是主版本号,minor是副版本号
四、库存在的意义
库是别⼈写好的现有的,成熟的,可以复⽤的代码,你可以使⽤但要记得遵守许可协议。
现实中每个程序都要依赖很多基础的底层库,不可能每个⼈的代码都从零开始,因此库的存在意义⾮同寻常。
封装
版本管理
共享库的好处是,不同的应⽤程序如果调⽤相同的库,那么在内存⾥只需要有⼀份该共享库的实例。
五、库的创建与使⽤
静态库的创建
Step 1.由源⽂件编译⽣成⼀堆.o,每个.o⾥都包含这个编译单元的符号表
Step 2.ar命令将很多.o转换成静态库,如
# ar crs libhello.a hello.o
共享库的创建
Step 1.由源⽂件编译⽣成⼀堆.o,每个.o⾥都包含这个编译单元的符号表
Step 2. 由编译器加特定参数编译⽣成共享库,如
# 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(const char *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
libtest.c
②编译⽣成⽬标⽂件(.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 libtests libtest.o ④結果$./libtesta
$ hello world
$./libtests
$ hello world
使⽤命令ldd查看依赖,结果如下:
动态加载库实例
①编写源⽂件
dlibtest.c
②编写
使⽤命令:$ gcc -o dlibtest dlibtest.c -ldl ③結果
$./dlibtest
$ hello world。