💐 🌸 🌷 🍀 🌹 🌻 🌺 🍁 🍃 🍂 🌿 🍄🍝 🍛 🍤
📃个人主页 :阿然成长日记 👈点击可跳转
📆 个人专栏: 🔹数据结构与算法🔹C语言进阶🔹C++🔹Liunx
🚩 不能则学,不知则问,耻于问人,决无长进
🍭 🍯 🍎 🍏 🍊 🍋 🍒 🍇 🍉 🍓 🍑 🍈 🍌 🍐 🍍
动态库的链接理解:
首先:你应该知道一个前提知识,每一个程序都有自己对应的进程空间,这个地址空间被划分为很多区域,例如:栈、堆、共享区~。
系统中有很多程序,这些程序会用到一些公共方法,将这些公共方法打包成动态库、共享库。首次使用到库时需要加载到内存中,之后其他程序在使用公共方法时,直接从内存中映射一份下来。不需要拷贝到自己的地址空间中,节省了内存。这种链接方式又叫动态链接
如下图,进程1和进程2都是用了同一个库中的方法,直接从内存中映射一份下来
链接的三种方式
- 静态链接:在程序运行之前,先将各个目标模块以及他们所需要的库函数连接成一个完整的可执行文件(装入模块),之后不再拆开。
- 装入时动态链接:将各目标模块装入内存时,边装入边链接
- 运行时动态链接:在程序执行中需要该目标模块时才对他进行链接,优点是便于修改和更新,便于实现对目标模块的共享。
问题一:谁来决定哪些库加载,哪些没加载?
os操作系统,记住一句话操作系统中的管理都是先描述后组织,加载进来的一个个库,操作系统会为它们生成一个结构体类似于PCB存储它的描述信息,然后对其进行一个双向指针连接,由操作系统来进行管理。
问题二:谈谈装入的三种方式
装入的三种方式
- 绝对装入:在编译时知道程序放在内存中的哪个位置,编译程序将产生绝对地址的目标代码。灵活性很低,只适用于单道程序环境,只有单道程序环境可以在程序运行之前就能确定程序将要放入哪个位置
- 静态重定位:又称为可重定位装入。装入模块全部使用逻辑地址,在装入的时候将逻辑地址全部转换为物理地址。
特点是必须分配其要求的全部内存空间,如果没有足够的内存,就不能装入作业。作业一旦进入内存后,在运行期间就不能再移动,也不能再申请内存空间。 - 动态重定位:又称为动态运行时装入。装入模块使用相对地址。相对地址到物理地址的转变要等到实际运行的时候才能确定。因此装入内存后所有的地址仍然是逻辑地址。这种方式需要一个重定位寄存器的支持。
重定位寄存器:存放装入模块存放的起始位置。当实际运行的时候实时地将逻辑地址转换为物理地址。采用动态重定位允许程序在内存中发生移动。可以将程序分配到不连续的存储区:在程序运行前只需要装入他的部分代码即可投入运算,然后在程序运行期间根据需要动态申请分配内存,便于程序段的共享,可以向用户提供一个比存储空间大得多的地址空间。
每个程序加载到内存中,都会有一个进程地址空间结构体,有栈区、堆区、共享区等等。它实际上就是一个个的有起始地址的空间组合在一起的一块空间。并不是连续的
首先,你的代码一定是保存在了磁盘上的,并且它会以绝对地址进行编址----又叫平坦模式。只需要拿到首地址,然后加上偏移量,
相对编址:就是每个区内的代码都是从0地址处开始编址
谈谈程序的加载的地址问题
你有没有想过一个问题,就是你写好的代码,cpu是如何找到你的代码所在位置的呢
1.你写的代码经过编译形成一个可执行程序。通常它会以绝对地址进行编址----又叫平坦模式
。
例如下图。一个可执行文件经过编译链接
后形成了一个可执行程序。
它包含
一个表头
和一堆二进制代码
。
为了方便我们看,使用反汇编
将 二进制代码装换成我们看的懂的汇编指令
观察上图,可以看到每一条汇编指令都有一个八进制的地址,fun1和fun2是两个函数,还有一个main函数去调用。 这些地址,又叫逻辑地址。
2 .根据可执行程序文件的表头(表头中包含了入口地址,每个分区的起始地址等
)填充进程的地址空间。,
3.从磁盘加载到内存,加载到内存后,又得到了一个物理地址,本身自带的就有逻辑地址。所有逻辑地址到物理地址的映射关系就有了。页表的左侧是逻辑地址,右侧是物理地址。
4.cup中的程序计数器pc指针用来保存正在执行指令的下一条指令
- 执行的过程如下图,蓝色线所示,从cup的pc指针找到要执行的指令,放入到指令寄存器中,然后cpu执行这个指令。
动态库是如何被进程动态链接的
首先,第一次用到库的时候肯定是 先需要加载到内存中的,加载到内存后,它也会有一个页表(逻辑-物理地址)映射关系。而且库的名称对应的有加载到内存的起始地址。
逻辑(虚拟)地址就能找到库
所在内存位置,加上偏移量
就能找到库中函数
的起始地址,所以,当一个进程用到库中函数时,它会将函数代码映射到程序地址空间的共享区中。其他程序在使用时也是一个道理,不需要拷贝到自己的代码区中,只需在共享区做个映射即可。