首先,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中。