首先,0地址处一般不会存放用户态程序代码,其次,call 40这里40的含义是绝对地址,指的就是内存中40的位置,绝对位置好吗?
上面分析已经知道了,对于程序地址的调用,应该采用相对地址,而非绝对地址,那么怎么把相对地址转换为绝对地址呢?
是么时候完成重定位? 编译时 载入时
编译时重定位就是在程序编译阶段对相对地址统一加上一个偏移地址,变成绝对地址,但是这样的坏处是,程序编译完毕后,程序中相关地址就变成了绝对地址,显然这样不够灵活,适合于小型,功能单一,不会变化的嵌入式系统
如果是载入时完成重定位,则是在挑选到一块空闲内存载入后,程序的基址就确定了,然后将程序中所有相对地址加上基址得到绝对地址。
小结:
内存中存放的是常驻程序,但是如果某个进程长时间阻塞,不使用CPU和内存资源,如果继续让该进程滞留在内存中,是不是有点浪费宝贵的内存资源呢?
因此每个进程都需要对应一个基地址,那么这个基地址应该存放在哪里呢?
每个进程都对应一个描述符,这个描述进程信息的数据结构被称为PCB,因此需要将base地址在程序开始运行时,将分配得到的基地址记录在PCB中。
当进程被换出,然后换入后,需要重新修改PCB中base地址的值。
CPU中有好几个基址寄存器,例如: x86系统中的ds代码段基址寄存器,用来存放当前程序的基地址,当程序每遇到一个相对偏移地址时,默认都会加上ds寄存器中保存的基地址,然后得到真实的物理地址。
当进程要进行切换时,会先将基址寄存器中的值保存到当前进程PCB中,然后将下一个进程保存的相关基地址状态拍到对应基址寄存器中。
由若干部分(段)组成,每个段有各自的特点、用途:代码段只读,代 码/数据段不会动态增长…
如mov [es:bx], ax
es是段基址寄存器,而bx存放的是段偏移地址。
程序分段放入内存能够更加高效的提升对内存的利用,例如: 如果不进行分段,如果程序栈空间不足,需要扩展,就需要将整个程序的代码重新copy到新分配好的更大的内存空间才行。
如果进行了分段处理,因为各段不干扰,因此只需要将栈段copy到新空间即可,不需要移动整个程序。
将程序分段存放后,程序的寻址方式也就发生了变化,需要分段寻址,即每次寻址就需要当前段号加上段内偏移,才能确定真实的物理地址:
因此进程的PCB中,还需要存放一个段表,该表内记录了每个段的段号,基地址,长度和其他访问权限管理等信息。
mov [DS:100], %eax
jmpi 100, CS
我们可以把操作系统看做是一个进程,而操作系统这个进程关联的段表就是GDT表。
每个进程也都关联了一个段表,这个表叫做LDT表。
因为我们对程序进程了分段,所以在程序载入到内存中时,是分段载入的,因此需要一个表来记录每个段的段号和该段对应的基地址,这就有了LDT表的诞生。
因此,每个进程在初始化创建时,需要初始化好当前进程对应的LDT表,该表内部记录了当前程序的分段信息,然后将初始化好的LDT表,设置到当前进程的PCB中。
当程序执行时,需要查询LDT表,得到当前段基址,然后与偏移地址拼接得到真实的物理地址。
当进程切换时,需要切换LDT表,并将当前CPU中相关基址寄存器的值存入当前进程PCB中。
版权说明 : 本文为转载文章, 版权归原作者所有 版权申明
原文链接 : https://cjdhy.blog.csdn.net/article/details/125952404
内容来源于网络,如有侵权,请联系作者删除!