Linux库文件

库文件的制作与使用

什么是库文件:

​ 库文件是计算机上的一类文件,提供给使用者一些开箱即用的全局变量、函数、结构、联合、枚举或类,它是若干个目标文件的集合,也可以对源码进行保密。库文件分为静态库和动态库,静态库和动态库的区别体现在程序的链接阶段和执行阶段。

静态库与动态库的区别:

​ 静态库在程序的链接阶段被复制到了程序中,动态库在链接阶段(*.o->a.out)没有被复制到程序中,而是在程序运行时由系统动态加载到内存中供程序调用,这是它们最本质的区别。

静态库的制作与使用:
创建静态库:
# 编译出目标文件:
gcc -c xxx1.c
gcc -c xxx2.c
...

# 把目标文件打包成静态库文件
ar -r libxxx.a x1.o x2.o ...

# ar 是一个专门控制静态库的命令(编译器子程序) 
	-r # 把目录文件合并成一个静态库,如果静态库文件已经存在则更新。
	-q # 向静态库中添加目标文件
	-t # 查看静态库中有哪些目标文件
	-d # 从静态库中删除目标文件
	-x # 把静态库展开为目标文件 
使用静态库:
# 方法1、直接调用,把共享库当作目标文件一样,与调用者的目标文件一起合并出可执行文件。前三位最好是lib
gcc main.c libxxx.a

# 方法2、通过设置LIBRARY_PATH环境变量来指定库的路径,通过-l参数来指定库名
gcc main.c -lxxx

# 方法3、通过gcc -L参数来指定库的路径,通过-l参数来指定库名
gcc main.c -L<PATH> -lxxx
动态库的制件与使用:
创建动态库:
# 编译出目标文件,-fpic编译出位置无关代码,在代码中使用相对地址,这样共享库就可以加载到内存的任何位置。
gcc -c -fpic xxx1.c
gcc -c -fpic xxx2.c
...

# 把目标文件打包成共享库:
gcc -shared xxx1.o xxx2.o ... -o libxxx.so
使用动态库:
# 方法1、直接调用,此方法生成的可执行程序必须在当时编译的目录下运行,否则找不到共享目录
gcc main.c libxxx.so

# 方法2、通过设置LIBRARY_PATH环境变量来指定库的路径,需要通
过-l来指定库名
#需配置LD_LIBRARY_PATH环境变量,不然编译时不会报错,程序运行时会提示找不到库文件
gcc main.c -lxxx
    
# 3、通过gcc -L参数来指定库的路径
gcc main.c -L<PATH> -lxxx

# 注意1:如果执行无法运行,需要检查操作系统是否能加载动态库,检查LD_LIBRARY_PATH(执行时在这里检查)环境变量
# 注意2:如果静态库和共享库同时存在,优先使用共享库,通过-static可以指定使用静态库,但是编译出来的可执行文件比较大。
动态加载共享库:
#include <dlfcn.h>(适合没有头文件只有库文件)
void *dlopen(const char *filename, int flag);
功能:打开共享库
filename:共享库的路径
flag:打开方式
	RTLD_LAZY:延迟加载,使用到共享库时再加载
	RTLD_NOW:立即加载
返回值:成功返回共享库的句柄,失败返回NULL

void *dlsym(void *handle, const char *symbol);
功能:通过函数名在共享库中获取函数指针
handle:共享库的句柄
symbol:函数名
返回值:函数地址,失败返回NULL

char *dlerror(void);
功能:获取错误信息

int dlclose(void *handle);
功能:卸载共享库
    
注意:编译时添加-ldl参数
使用库文件时的辅助工具:
# 查看目标文件、可执行文件、静态库、共享库文件的符号列表。
nm <flie>

# 删除目标文件、可执行文件、静态库、共享库文件中符号列表、调试信息,可以有效降低文件的大小。
strip <file>

# 查看可执行文件依赖了哪些共享库
ldd <file>
   
# 把二进制文件的内容转换成汇编代码,就是传说中的反汇编,但如果二进行文件进行加密机制则无法成功转换
objdump -d <file>
静态库的优点:
使用方便:

​ 在链接目标文件生成程序时,只需要把静态库与目标文件一起编译,编译器就会把目标文件中使用到的静态的内容拷贝到程序中,程序在运行时就不需要静态库了。

运行速度快:

​ 静态库文件内容拷贝到程序中,因此在程序运行过程中,使用到静态库中的函数、变量时,只会在程序的内部跳转,因此使用静态库的程序要比使用动态库的程序运行速度快。

静态库的缺点:
更新麻烦:

​ 如果静态库中的内容发生了改变,如:版本升级,修改BUG,那么使用了相关静态库的程序就需要重新编译,如果是应用程序,用户就需要重新下载。

浪费内存:

​ 假如有一个叫libxxx.a的静态库,a.out、b.out、c.out程序都使用使用它,那libxxx.a的内容会被分别拷贝一份到a.out、b.out、c.out,当这三个程序运行时,libxxx.a静态库文件中的内容就有三份存在于内存中,这样就有了冗余,造成内存的浪费。

总结:

​ 当一个模块不会再发生改变,并且执行速度有一些要求,适合把它封装成静态库。

动态库的缺点:

​ 在链接目标文件时,虽然动态库需要与使用它的目标文件一起编译,但编译器只是记录被调用的内容(函数、变量)在动态库中的位置,生成的程序中只有跳转到动态库的相关指令,当程序运行时需要把动态库一起加载到内存,当执行到动态库的相关内容时,才跳转到动态库所在的内存中执行,完成后再返回,这样会导致两个问题。

运行速度慢:

​ 使用动态库程序需要在程序和动态库中来回跳转,因此要比使用静态库的程序运行的速度慢。

程序无法执行:

​ 如果程序使用了动态库,当它运行时就需要系统把它使用的动态库一起加载到内存,如果系统找不到相应的动态库,那么程序就无法运行(Windows系统经常提示的xxx.dll文件缺失,程序无法运行),产生这种错误的原因有很多,如:环境变量配置错误,动态库文件存储位置错误,可执行文件拷贝到其它计算机上运行时没有一起拷贝动态库文件。

动态库的优点:
节约内存:

​ 使用的动态库只需被系统加载一次(加载到操作系统),不同的程序都可以使用到内存中的动态库,因此节约了很多内存,由于多个程序可共享使用一个动态库,所以动态库也叫共享库。

更新方便:

​ 如果动态库中的函数格式没有变化(返回值、函数名、参数列表),而只是函数中的业务逻辑代码发生变化,那么只需要重新编译动态库即可,不需要重新编译相关的可执行文件,这也是某些应用程序可以自动更新的原因。

总结:

​ 随着计算机性能的不断提升,弥补了动态库运行速度慢的缺点,再加上它能节约内存、更新方便,最主要的是计算机硬件一直在升级,所以就导致大多数代码需要不断的升级,因此我们大多数情况下把模块封装成动态库。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值