您的位置:首页 > 运维架构 > Linux

Linux 下地址映射过程总结

2017-11-13 18:13 841 查看
CPU上电强制进入实模式

实模式下 访问地址 DS<<4 + ip = 物理地址

主要的段寄存器有

DS:数据段

SS:堆栈段

CS:代码段

ES:扩展段

GS:全局段

IP:偏移量

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

GDTR 指向了 GDT 全局描述附表

LDTR 指向了 LDT 局部描述符表

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

如图


这时段寄存器不在用于存放地址的去掉后4位(因为之前内存段的起始地址总是4k整数倍,因此后四位都为0),而用于存放段描述表的索引。

比如想要访问数据段的地址,DS存放的是数据段在GDT中对应的下标。



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

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



因此保护模式下 内存分段的地址映射

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

即可结束程序



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

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

LDTR内容:0x0068

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

说明LDT在GDT 13位存着

找到GDT的第13号元素内容



得到:0xe2d00068 0x000082fa

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

SS内容 :0x0017

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

说明详细信息在LDT[2]



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

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

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

此时分段映射结束

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

ps.

CR0 最高PG位 0表示没有开启分页机制 1表示开启

CR2 发生缺页异常的虚拟地址

CR3 页目录的虚拟地址

CR4 PAE位物理地址扩展 0未开启 1开启



CR0 8=》1000 最高位为1 开启内存分页

将我们线性地址0x 13fffef4 拆分为10 10 12



0001001111 1111111111 111011110100‬ 二进制数

79 1023 3828 对应数值



得到页目录地址0x00fea027

前20位有效=》0x00fea000

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



得到页表地址0x00f94067

前20位有效=》0x00f94000



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

值为a == 10



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

从而data = 0;

continue程序

对应while判断为false

退出程序



到这该死的地址映射过程就算完成了。。。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  linux