Linux中动静态库的生成与使用
0 定义
库可以看作是代码仓库,库里面封装了很多方法,当客户(也可以指程序员)需要使用时,可以直接导入库,然后调用库中的方法,可以很好的提高编程效率
1 动静态库的区别?
动静态库最大的区别除了体现在名字上(?)主要体现是在它们生成程序的链接阶段的时候。在链接阶段时,静态库会将自己的代码全部复制进程序中;而动态库则会进行分批加载
ps.linux中静态库的后缀名是.a 动态库的后缀名是.so
分批加载
在链接前,磁盘、地址空间、内存关系如下图所示:
此时,a.out被加载到内存上
1.链接时,也就是需要调用动态库时,动态库会先加载到内存上。
2.如果程序碰到了需要使用的库中的方法,内存中的那个方法就会建立与页表间的链接
3.此时代码会被加载到地址空间的共享区(栈与堆之间)里面
4.当代码区里面的代码需要调用方法时,就会到共享区中调用
ps.正是因为动态库中的文件是分批加载到共享区中的,所以如果多个程序使用这个库,那么他们可以共享库中的方法(都到一个地方去找),就不像静态库傻乎乎的全部复制到每一个程序代码中,更加节省空间。不过调用也会比静态库更慢,因为内存和地址空间之间需要建立映射关系,所以加载动态库的程序运行速度相对较慢。
结束了理论时间就来看看代码!
2 动静态库的链接(代码实操) 前期准备
首先我们先写个hello.c、math.c文件,准备把它编辑成库,代码如下:
并在它们的头文件中声明这两个函数(这边省略),我们在写一个含有main函数的main.c来调用它们:
#include "mymath.h"
#include "hello.h"int main()
{int x = 1;int y = 2;printf("x+y = %d\n",mymath(x,y));hello();return 0;
}
并把hello.c和.c生成为后缀为.o的目标文件
gcc -c hello.c -o hello.o
gcc -c mymath.c -o mymath.o
静态库的生成与使用
我们用ar(归档)来生成静态库:
//lib+文件名.a 后面跟.o文件
ar -rc libmylib.a hello.o mymath.o
此时文件中就生成了一个.a的静态库文件
我们把头文件和库放进另一个文件夹中,并把打包前的原.c和.o文件放到别的地方去(虽然打完包后不需要,但是后面编译动态库还需要),我们来模拟一下使用库的情况:
那么我们该怎么使用静态库呢?
//-I 后面跟着头文件的路径
//-L 后面跟库的路径
//-l(小L)后面跟要使用的库名(不用加后缀也不用加前面的lib)
gcc main.c -o main -I ./a/include -L ./a/lib -lmylib
编译成功后是可以运行的:
系统路径下的头文件存放在:/usr/中
系统路径下的库文件存放在:/usr/lib64中
//到刚刚的include目录中,把.h文件复制进系统路径中
sudo cp *.h /usr/include//复制库到系统路径中
sudo cp *.a /usr/lib64
复制后,就可以直接编译main.c文件并执行(记得也要指定库名!)
动态库的生成与使用
动态库的生成采用以下代码:
//fPIC 是告诉编译器要生成位置无关的代码 这是动态库的一个条件之一
//shared 告诉编译器生成的是.so动态库文件,而不是一个可执行文件的别名
gcc -fPIC -shared -o libmylib.so hello.c mymath.c
生成后,我们把动态库的库文件和头文件同样的放入其他的文件夹边:
如何使用动态库?
//你自己存放库的地址
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/zsy/class/io/ku/so/lib
然后我们就可以直接编译生成可执行程序了,不过还是要指定库名。生成后我们再使用ldd(列出动态依赖关系)命令查看该可执行程序的动态依赖关系,可以看出连接的是动态库
这个方法有不好的一点就是再次启动终端时就无法编译了,命令会重置,所以我们可以看看另一种方法
我们可以在路径/etc/ld.so.conf.d/里面创建自己的conf文件,并将库路径复制到里面,然后我们就可以永久使用了
//创建mylib.conf文件到/etc/ld.so.conf.d/中
sudo touch /etc/ld.so.conf.d/ mylib.conf
/ect/ld.so.conf.d中的文件:
然后我们 vim mylib.conf 把库所在路径复制进去(记得sudo!!)
然后我们就可以直接编译咯(当然还是要指定库名)