- 我最近开了几个专栏,诚信互三!
====> |||《算法专栏》::刷题教程来自网站《代码随想录》。|||
====> |||《C++专栏》::记录我学习C++的经历,看完你一定会有收获。|||
====> |||《Linux专栏》::记录我学习Linux的经历,看完你一定会有收获。|||
====> |||《C#专栏》::记录我复习C#的经历,深度理解,查漏补缺,不定期更新。|||
====> |||《计算机网络专栏》::记录我学习计算机网络,看完你一定会有收获。|||
详解库和程序运行过程
- 库的原理
- 如何分装一个库
- 静态库
- 动态库
- 可执行文件的编址
- 程序调用库的原理
库的原理
库分为动态库和静态库,它们都是.o文件的集合,linux系统中,静态库以lib为前缀.a为后缀, 动态库以lib为前缀.so为后缀。
如何分装一个库
我们可以使用一个指令分装静态库。
ar -rc [分装成库的名字] [要分装的.o文件]
分装动态库使用以下指令。
gcc/g++ -shared [分装成库的名字] [要分装的.o文件]
分装动态库,编译生成.o文件时需要加入-fPIC选项。
gcc -fPIC -c [.c]
静态库
静态库的运行原理是将库内的所有内容拷贝到运行的程序中,静态库的运行与库的位置无关。
gcc运行静态库需要使用选项-static。
动态库
动态库的运行原理是执行程序调用加载到内存中的公共库,动态库的运行与库的位置有关系。
gcc默认运行文件运行动态库。
为了链接动态库,需要了解一些选项。
-l:链接动态库,只需要去掉lib和.so。
-L:链接库所在的路径。
-I:头文件所在目录
链接动态库是有固定路径的,我们直接运行一个程序,调用动态库去/usr/lib目录下寻找,我们链接我们自己写的库,我们有3个方法。
1.直接在/usr/lib目录下创建一个库的同名软连接。
2.环境变量中存在LD_LIBRARY_PATH,可以将库所在的目录改入环境变量中。
3.ldconfig 配置/etc/ld.so.conf.d/,ldconfig更新。
可执行文件的编址
文件在内存中其实是会被编址的,其中的所有地址都是虚拟地址,在当今的计算机中,我们的编址是平坦模式。
在可执行文件的头部存在很多信息,如main函数的入口地址,和该程序所引用的库的名字。
上图为平坦模式编址。
在可执行文件加载到内存中时,可执行文件中的虚拟地址就是进程地址空间中的虚拟地址,可执行文件被加载到内存中时,操作系统也可以知道可执行文件加载到内存中的物理地址,这样我们就知道了虚拟地址和物理地址,这样页表中的映射内容也被填满了。
在cpu中存在pc指针和IR指针,pc指针指向当前指令的下一行指令,IR寄存器中存储的当前执行的指令内容。
实际上,pc指针中取出的地址也是虚拟地址,随后通过页表映射为物理地址,形成一个循环,这样整个程序就运行起来了。
整个程序的运行其实是由操作系统,编译器,计算机体系结构三者配合完成的。
程序调用库的原理
静态链接直接将静态库加载到可执行文件中,没有特殊的情况。
但是当可执行文件中要使用动态库的内容,则需要将动态库也加载到内存中,而动态库中的内容也会被映射到进程地址空间的共享区中。
动态库在磁盘中,也会被编址,并且操作系统会对库进行管理,而在可执行文件中,调用库函数是使用库函数名+库函数中该函数的偏移量,来标识该函数,在加载到内中时,库函数名就会被解释为库函数的虚拟地址,随后就通过页表找到物理地址,从而调用函数。