动静态库
动态库和静态库
静态库(.a):程序在编译链接时把库的代码链接到可执行文件中,程序运行时不再需要静态库
动态库(.so):程序在运行时才去链接动态库的代码,多个程序共享使用库的代码
一个与动态库链接的可执行文件仅仅包含他用到的函数入口地址的一个表,而不是外部函数所在目标文件的整个机器码
在可执行文件开始运行以前,外部函数的机器码由操作系统从磁盘上的该动态库中复制到内存中,这个过程称为动态链接
动态库可以在多个程序间共享,所以动态链接使得可执行文件更小,节省了磁盘空间,操作系统采用虚拟内存机制允许物理内存中的一份动态库被要用到该库的所有进程共用,节省内存和磁盘空间
先完成一个小任务,在一个目录中完成两个函数的实现,然后将其二进制可执行文件.o还有头文件.h拷贝到另一个目录中,使用它自己的main函数来完成最后的链接执行这个两个函数
加减函数的实现和头文件如下
通过 gcc -c生成可执行二进制文件
将所有 .o文件和头文件.h拷贝到另一个目录中
main.c
链接最后运行程序
所以如果我们不想给对方自己的源代码,只需要提供对方 .o方法的实现, .h包含的方法,对方便可使用自己的代码进行链接最后执行
如果有很多方法,那就需要拷贝多份 .o文件,是不是很麻烦呢???所以想了个办法,将所有的 .o文件进行打包,只提供对方一个文件,而这个文件也称为库,库又分为静态库和动态库,接下来就来学习吧
生成静态库
先介绍两个指令
ar:是归档指令, archive将文件打包到一个文件中
rc:表示 replace and create
这只是将库文件进行了打包,在上面的例子中,还包括了头文件,所以还需要进行改进
output:称作发布版本,打包之后便可进行发布
自己生成的静态库便完成了,只需要进行发布即可
将库进行压缩,发布
在测试目录中进行库的下载(也就是拷贝),解压
在进行链接时,Linux有些不同,在之前的学习中,链接阶段都是现在当前程序中寻找头文件,然后再到其他程序中寻找头文件;还需要告知编译器,当前库所在路径,不经如此还要告知其库的名称,当然在之前的学习中这一切都是编译器默认所做到
所以,链接操作如下
-I ./mylib/include:告知编译器所包含的头文件
-L ./mylib/lib:指明库所在路径
-l math:指明库名称
还剩最后一个小问题,观察下列指令
为什么 mymath是动态链接的,并且编译器所罗列的库中并没有刚刚自己所写的库,这是为什么呢???
gcc默认是动态链接的,如果只有一个库,最后是动态链接还是静态链接却决于这个库的类型;但是如果存在很多库,当链接时如果使用了一个动态库,则就是动态链接,上面在最后链接时,库只提供了静态库,而默认是动态链接,所以只能将将代码拷贝到可执行程序中使用动态库进行链接
生成动态库
与静态库类似,有一点区别
在生成.o文件时,需要加上fPIC生成位置无关码
进行文件打包,这里可以直接使用gcc再加上shared生成共享库格式即可
分别将头文件和库文件放入相应的目录中,拷贝给测试目录
最后进行链接执行程序
这里与静态库有所不同,动态库已经加载到程序中,但是并没有找到;在链接时,我们已经将库文件,路径和库名称都告知了gcc,当程序链接之后,与编译器就无关了;运行时操作系统和命令行解释器也是需要知道库所在的位置,但是由于库并没有在系统的默认路径下,所以操作系统无法找到,程序也就无法进行
解决措施也很简单,只需要将库路径添加到默认搜索路径即可,在当前目录中进行软链接,程序即可运行
动静态库的加载
静态库
静态区并不是加载到内存中的,在程序运行时所需要使用的函数被拷贝到程序的代码段中,且必须按照相对确定的地址进行编址,称作绝对编址;再由程序拷贝到内存中,再通过虚拟地址空间映射到代码段上进行访问
图解如下
动态库
动态库的加载是相对位置的加载,在程序中存在着库中函数的偏移量,这也解释了为什么上面使用 fPIC生成位置无关码;程序加载到内存中的代码段,通过页表映射到虚拟地址空间的代码段中,当程序执行到库函数时,此时程序中只有函数的偏移量,进程便停止,将动态库加载到内存中,再通过页表映射到虚拟地址空间中的共享区,一旦库函数加载到共享区中,起始位置就确定了,然后程序通过偏移量在库中寻找对应的函数,至此动态库加载的内容结束