CPU上电强制进入实模式
实模式下 访问地址 DS<<4 + ip = 物理地址
主要的段寄存器有
DS:数据段
SS:堆栈段
CS:代码段
ES:扩展段
GS:全局段
IP:偏移量

实模式下访问内存是极其不安全的,我们访问内存时,不仅要知道内存段的起始地址,还需要知道内存段大小和访问权限。但是这些信息没有办法都存在16位的段寄存器中,从80386开始,有了保护模式,又增加了几个寄存器,为了解决信息没有办法都存在16位的段寄存器中的问题,GDTR和LDTR。

GDTR 指向了 GDT 全局描述附表

LDTR 指向了 LDT 局部描述符表

GDT是一个数组 全局的段描述表

如图

openresty lua 地址映射 linux做地址映射_物理地址


这时段寄存器不在用于存放地址的去掉后4位(因为之前内存段的起始地址总是4k整数倍,因此后四位都为0),而用于存放段描述表的索引。比如想要访问数据段的地址,DS存放的是数据段在GDT中对应的下标。

openresty lua 地址映射 linux做地址映射_寄存器_02

其中8192个描述符中12项被系统预留下来了 因此8192 - 12 = 8180 个描述符

内核代码(12个预留项):

openresty lua 地址映射 linux做地址映射_linux_03

因此保护模式下 内存分段的地址映射
GDT[DS>>3].BaseAddr + IP(逻辑地址,加前要与Length比较) = 线性地址
若没有开启分页机制 线性地址 == 物理地址
若开启分页机制 线性地址 == 虚拟地址
需要多级的页表映射 虚拟地址才能转化到====》 物理地址

那么接下来通过bochs对简单程序的调试来熟悉Linux32位系统的二级映射过程
程序源码:

#include <stdio.h>

int main()
{
int data = 10;
pritnf("&data = 0x%x\n",&data);
whlie(data);
printf("progrma done...\n");
return 0;
}

可见,程序运行期间,会在while函数体内不断循环

在不改变代码的前提下,通过二级映射,访问data的物理内存,修改为0

即可结束程序

openresty lua 地址映射 linux做地址映射_寄存器_04


sreg指令可以看到段寄存器的信息

data是局部变量,因此ss寄存器的内容是映射的关键LDTR内容:0x0068

二进制形式展开 0000 0000 0110 1(序号13) 0(GDT) 00(内核态)

说明LDT在GDT 13位存着

找到GDT的第13号元素内容

openresty lua 地址映射 linux做地址映射_物理地址_05


得到:0xe2d00068 0x000082fa

需要注意是小端模式,对应段描述符表项的定义 得到地址0x00fae2d0 (LDT的起始地址)

SS内容 :0x0017

二进制形式展开 0000 0000 0001 0 (序号2) 1(LDT) 11(用户态)

说明详细信息在LDT[2]

openresty lua 地址映射 linux做地址映射_openresty lua 地址映射_06


得到:0x00003fff 0x10c0f300(data栈内存的详细信息)

对应段描述符表项的定义,0x10000000栈内存起始地址

加上程序打印出的虚拟地址0x10000000+0x3fffef4 得到线性地址

此时分段映射结束


通过creg指令查看CR0的最高PG位 查看有没有开启分页式管理

ps.
CR0 最高PG位 0表示没有开启分页机制 1表示开启
CR2 发生缺页异常的虚拟地址
CR3 页目录的虚拟地址
CR4 PAE位物理地址扩展 0未开启 1开启

openresty lua 地址映射 linux做地址映射_linux_07


CR0 8=》1000 最高位为1 开启内存分页将我们线性地址0x 13fffef4 拆分为10 10 12

openresty lua 地址映射 linux做地址映射_openresty lua 地址映射_08

0001001111 1111111111 111011110100‬ 二进制数
79 1023 3828 对应数值

openresty lua 地址映射 linux做地址映射_物理地址_09

得到页目录地址0x00fea027
前20位有效=》0x00fea000
(为什么说前20位有效呢,PT起始地址为4K的整数倍,因此32位地址的低12位都为零,可以不用存储,用来存取其他内容,如访问权限等等,特别指出 pt的最后一位是present位,0代表对应的物理页面在交互分区中,1代表对应的物理页面不在交互分区中,这又牵扯到了swap分区了,后几期会专门写一篇理解)

openresty lua 地址映射 linux做地址映射_寄存器_10

得到页表地址0x00f94067

前20位有效=》0x00f94000

openresty lua 地址映射 linux做地址映射_物理地址_11


得到data映射到物理内存的地址

值为a == 10

openresty lua 地址映射 linux做地址映射_openresty lua 地址映射_12


通过setpmem语句 将data对应的物理地址设置为4字节的0

从而data = 0;

continue程序

对应while判断为false

退出程序

openresty lua 地址映射 linux做地址映射_虚拟地址_13

到这该死的地址映射过程就算完成了。。。