我会尽力以最简洁清晰的思路来写这篇文章。
所谓内存寻址也就是从写在指令里的地址,转化为实际物理地址的过程。因为操作系统要兼顾许多东西,所以也就变得复杂。
逻辑地址 → 线性地址 → 物理地址
逻辑地址 = 段 + 偏移量
因为:最开始cpu中的alu宽度只有16位,但地址总线宽度有20位。所以设置四个段寄存器:cs(指令),ds(数据),ss(堆栈),es(其它)。
每个段寄存器16位,对应地址总线高16位。每条指令中的16位内部地址与某个段寄存器中内容相加,得到20位的实际地址。
上述的16位内部地址到20位实际地址的转换还是8086的时代。
到了80386,32位cpu的时代,许多情况都发生改变。其中一点:要实施“保护模式”。还要兼容过去的段寄存器。
于是,之前的公式从新写为:
逻辑地址 = 段选择符(16位) + 偏移量(32位)
所以对80386cpu的寻址还是要基于从前8086寻址方式来理解,《linux内核源代码情景分析》中这样描述:
可简单记为:段寄存器(段选择符) → 地址段描述结构(段描述符) → 基地址 → 指令中发出的地址 + 基地址 = 物理地址
地址段描述结构(段描述符) 从何而来:在cpu中增设两个寄存器,一个是全局性的段描述表寄存器GDTR;一个是局部性的段描述表寄存器LDTR。
段描述符地址 = 段选择符index字段×8 + gdtr/ldtr寄存器中的值
这张图可以表现,怎样由段寄存器(段选择符)得到段描述符 。
段选择符得到“段”的过程,一种是访问段描述表得到段描述符。
另外还可以通过非编程寄存器,不访问段描述表得到段描述符。
总之,是段选择符(段寄存器) → ( 段描述符表 → )段描述符 → 基地址的过程。
段描述符:
继续推导之前的式子:
段描述符的获得:段选择符index字段×8 + gdtr/ldtr寄存器中的值
基址 = 段描述符Base字段
逻辑地址 = 段选择符(16位) + 偏移量(32位) = 段描述符Base字段 + 偏移量
参考资料:
《汇编语言》王爽
《linux内核源代码情景分析》毛德操
《Understanding the Linux Kernel》Daniel P. Bovet / Marco Cesati