络腮胡菲菲 发表于 2024-8-12 16:14:20

【Linux】详解动态库链接和加载&&对可实行程序底层的明确

一、动静态库链接的几种情况 

   
[*]如果我们同时提供动态库和静态库,gcc默认使用的是动态库。如果我们非要使用静态库,要加-static选项。
[*]如果我们只提供静态库,那可实行程序没办法,只能对该库进行静态链接,但程序不一定团体式静态链接的。
[*]如果我们只提供了动态库而我们要进行静态链接会发生链接错误,程序此时默认只能进行动态链接。
二、明确动态库加载

         我们的动态库默认就是一个磁盘级别的文件。当我们的程序开始运行时,当程序运行到需要用到库中的实现方法时,库的代码和数据就会被加载到物理内存当中。库的实现方法一定是要跟程序运行起来所形成的历程产生关联的,动态库加载后,会被映射到该历程的地点空间中,准确来说,是先在页表中填写好对应假造地点和物理地点之间的映射关系,才被映射到历程地点空间中的共享区中。
        如果此时另一个历程也要加载该动态库,只需要填写它的页表的映射关系即可,不需要再从磁盘中加载一份动态库代码和数据,如果所需库不在内存中才需要加载。这样就保证了一个动态库最多只在内存中存在一份,大大节省了内存开销。这里的本质就是说其实所有体系历程中公共的代码和数据,只需要存在一份。
https://i-blog.csdnimg.cn/blog_migrate/5d8eff8dbde72cf11b928904be1761aa.png
三、可实行程序的明确

3.1、可实行程序中地区的分别

        可实行程序本身是有本身的格式信息的。我们的可实行程序在被加载到内存中之前,程序当中本身就有地点,可实行程序的每一行都是被编址的。我们的可实行程序在没有加载之前,也已经基本被按照类别(好比权限,访问属性等),已经将可实行程序分别成了各个地区。
https://i-blog.csdnimg.cn/blog_migrate/cfc21c29665afc4648de5f255beebd92.png
   text (或 code):


[*]这部分包含了程序的机器代码,即CPU实行的指令。
[*]通常,text (或 code)的巨细表示了程序中的指令数量。
data:


[*]这部分包含了程序中已经初始化的全局变量和静态变量的值。
[*]data段的巨细表示了程序中已初始化数据的巨细。
bss:


[*]这部分用于存储未初始化的全局变量和静态变量。
[*]与 data不同,bss段在程序加载到内存时并不包含实际的数据值,而是只预留了足够的空间。这些变量的初始值通常是0。
dec (decimal):


[*]这通常表示某个段或整个程序巨细的十进制表示。
[*]当你看到dec 值时,它可能是text、data、bss或整个程序巨细的十进制表示。
hex (hexadecimal):


[*]这表示某个段或整个程序巨细的十六进制表示。
[*]与 dec雷同,hex值可能是 text、data、bss或整个程序巨细的十六进制表示。
        可实行程序中除了上述说到的信息外,可实行程序的头部信息(表头)包含了关于程序实行所必需的一系列元数据和布局信息, 头部信息中就保存了main函数的起始地点,所以我们的可实行程序本身就知道要从那里开始实行。
3.2、可实行程序的编址

        首先在这里需要跟各人明确一个概念,我们历程地点空间中的很多地点数据,都是从可实行程序中来的。将我们可实行程序中的每一行都以绝对编址的方式进行编址,每一行就对应一个地点,这就相当于我们历程地点空间中的假造地点。也就是说,假造地点需要我们的操作体系、编译器和计算机体系布局共同共同才能形成。当我们的静态库被我们的可实行程序加载时,静态库的代码就要被写到我们可实行程序text部分,静态库代码在text部分的绝对编址是确定的,当然静态库数据的编址也是确定的。
3.3、明确动态库动态链接和加载

        在历程在被创建的时间是要先创建PCB,初始化它对应的历程地点空间,然后再将磁盘中的数据加载到内存的。为了初始化历程地点空间,操作体系读取可实行程序的表头。在磁盘中存放着的可实行程序的表头中就包含了关于程序实行所必需的一系列元数据和布局信息,操作体系通过读取表头中的信息初始化对应地点空间中的正文代码,已初始化数据和未初始化数据,所以我们也就能明确了为什么不同可实行程序加载完历程地点空间中有数据的地点范围不一样了。
        当可实行程序的代码和数据加载到内存中时,由于可实行程序的每一行对应一个假造地点,加载到内存中又有了一个实际的物理地点,拿着物理地点和假造地点操作体系就可以填写页表对应的映射关系了。当这个历程要被CPU调度实行时,CPU的pc指针就保存了main函数的入口地点(pc指针内保存的是假造地点),CPU内部可以帮我们读取页表的内容将假造地点转换为物理地点,CPU去到相应的物理地点取到指令放到CPU内部的指令寄存器中,通过剖析指令完成工作或是指明下一个假造地点,再让CPU剖析假造地点找到物理地点中的内容,这样周而复始,我们的程序就跑起来了。
https://i-blog.csdnimg.cn/blog_migrate/65abb8d67bc08d6df257fd220041bf12.png
        现在假设我们的可实行程序调用了某一个动态库,在我们程序的就会有这个动态库的调用信息(好比说_start(这里的_start可以看成库名,最后会被转换成库在共享区中的地点)+0x10,print,就是指我们的可实行程序调用了动态库中相对动态库首地点偏移量为0x10处的print函数),可实行程序的表头会记载调用了这个动态库。 我们调用的动态库也是要被加载到内存中的,并被映射到历程地点空间的共享区中。当我们的程序实行到动态库调用处,就会根据动态库首地点加偏移量找到页表中在内存中的物理地点,进而就能调用动态库中的方法了。所以库中代码和数据的访问,都是可以通过库在地点空间上的起始地点加上我们程序内部的偏移量就可以访问。
https://i-blog.csdnimg.cn/blog_migrate/650392afe20db1a0d018fce98b515fa5.png

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: 【Linux】详解动态库链接和加载&&对可实行程序底层的明确