文章目录
- 前言
- 一、静态库
- 1.静态库的创建
- 2.静态库的链接
- 3.将库进行打包
- 4.链接方法:
- 1.直接链接
- 2.拷贝到系统路径里面
- 3.采用软链接方法
- 二、动态库
- 1.解决加载找不到动态库的方法
- 1.直接拷贝
- 2.建立软链接
- 3.建立自己的动态路径配置文件
- 2.为什么动态库权限需可执行而静态库没有
- 三、关于地址
- 1.程序没有加载前的地址(程序)
- 2.程序加载后的地址(进程)
- 3.动态库使用
前言
库本质就是把一堆(.o)后缀的文件也就是目标文件整合在一起
一、静态库
静态库(.a):程序在编译链接的时候把库的代码链接到可执行文件中。程序运行的时候将不再需要静态库
1.静态库的创建
[root@localhost linux]# gcc -c mymath.c -o mymath.o
先把文件变成目标文件
生成静态库
[root@localhost linux]# ar -rc libmymath.a mymath.o
ar是gnu归档工具,rc表示(replace and create)
格式:
ar -rc 静态库名字(注意格式) 静态库所包含的目标文件
2.静态库的链接
3.将库进行打包
在makefile中
1.第三方库的使用,gcc往后必须加上
-l +库名
2.如果系统中只提供静态链接,则gcc对其进行静态链接
3.如果系统中链接多个库,则直接在gcc后面加上指定路径和库的名字就可以
4.链接方法:
1.直接链接
2.拷贝到系统路径里面
一般系统里头文件都在
/usr/include
库都在/lib64
将我们的库放到系统路径下,我们就不需要在编译的时候指定我们的路径了,因为系统会自己到系统路径中找,但我们还是要带上我们的库名的,这是规定
3.采用软链接方法
先为头文件部分以及库文件创建软链接放入系统路径中去
编译链接
二、动态库
动态库(.so):程序在运行的时候才去链接动态库的代码,多个程序共享使用库的代码
//生成目标文件,需要带-fPIC
[root@localhost linux]# gcc -fPIC -c myprint.c mylog.c
//创建动态库
[root@localhost linux]# gcc -shared -o libmymath.so *.o
我们前面只告诉了编译器我的动态库在什么位置,但当我程序运行起来的时候就和编译器没关系了,所以我们也要告诉系统(加载器)我的动态库放在哪里!!!
静态链接能找到是因为我们静态库直接整个干到可执行文件里面了,根本不需要去找静态库,因为已经和我原来的可执行程序融为一体了
1.解决加载找不到动态库的方法
1.直接拷贝
和静态库类似,直接拷贝到系统默认库路径 /lib64
2.建立软链接
3.建立自己的动态路径配置文件
ldconfig 配置/etc/ld.so.conf.d/,ldconfig更新
不认动态库名,只需放入动态库路径,因为当时我们告诉过编译器我们用哪个库了
将动态库路径写入创建的conf文件
重新ldconfig后,我们就能找到动态库的路径,而且是全局有效
2.为什么动态库权限需可执行而静态库没有
因为我们静态库直接拷贝到源文件中,拷贝完成后,后续程序运行的时候就用不到了,因为我内部已经有了,而我动态库要以可执行程序的形式加载到内存中与可执行程序产生关联,静态库不需要加载到内存
三、关于地址
动态库在内存中加载会被所有程序共享,动态库在运行的时候是要被加载到内存中的
1.exe加入到物理内存后与页表建立映射关系,当需要使用动态库的时候,动态库也会用类似的方法加入到内存中,之后2.exe也要使用和1.exe相同的动态库,操作系统知道库的加载情况,检测到那个库已经加载到内存里面不需要再加载一遍了,所以直接改变进程2的页表,让动态库与其共享区建立联系
1.程序没有加载前的地址(程序)
编译器也要为操作系统考虑,程序编译后之后,内部就已经有地址了,这里的地址现在来看其实就是虚拟地址
2.程序加载后的地址(进程)
问:如何执行我们的第一条命令呢?
答:
其实我们在编译好文件的时候,文件中会有一个entry即入口地址存在于表头,我们CPU中的PC会载入程序的入口地址,这个入口地址是虚拟地址,所以我们要借助页表去寻找其物理地址,但因为此时我们入口地址对应的物理地址还未写入页表,这个时候就会发生缺页中断,该程序会加载到物理内存中,物理地址与虚拟地址的映射关系也会被写入页表中,这样就完成了一个循环过程,我执行下面的指令如果没有对应的物理地址会发生缺页中断写入页表
重点:
⭕与此同时,操作系统会为程序对应的进程构建进程地址空间,并让进程中的指针指向其对应的进程地址空间。
当CPU要运行这段进程时,操作系统会把地址空间代码区的code_start送入CPU,CPU进入代码区后从main函数作为入口开始执行程序
🚩而因为编译器在编译程序代码内部的逻辑地址时,同样也是遵循进程地址空间的规则来编址的,所以当为进程构建地址空间时,其地址空间中的各个虚拟地址是直接使用编译器在编译可执行程序时生成的逻辑地址。
所以当CPU要开始执行程序时,是拿到了main函数的虚拟地址,再通过页表映射到物理内存中的物理地址,找到main函数加载到内存中的代码数据并运行,而在main函数运行的过程中进行了fun函数的调用,这时CPU读取到的依旧是fun函数的虚拟地址(可执行程序中的逻辑地址),然后再次通过页表映射到物理内存中的物理地址
⭕CPU在运行时,读取的都是指令,指令中便包含了地址,而自始至终,CPU读取的都是虚拟地址,没有见过物理地址
3.动态库使用
问:我们可执行程序,如何找到动态库中我们要使用的特定部分呢?
*
答:页表中会存储动态库的起始位置的虚拟地址与物理地址的链接,动态库直接采用偏移量对库中函数进行编址,我们知道printf的偏移量后再知道动态库的起始地址,就能再动态库中找到printf