利用bochs将逻辑地址转化为物理地址理解操作系统的分段分页机制

时间:2022-02-24 09:19:59
在步入正题前,我们先来了解几个概念:逻辑地址、线性地址、虚拟地址、物理地址。
逻辑地址(logical address):Intel为了兼容,将远古时代的段式内存管理方式保留了下来。逻辑地址指的是机器语言指令中,用来指定一个操作数或者是一条指令的地址。
线性地址/虚拟地址(linear address/virtual address):跟逻辑地址类似,它也是一个不真实的地址,如果逻辑地址是对应的硬件平台段式管理转换前地址的话,那么线性地址则对应了硬件页式内存的转换前地址。
物理地址(physical address):用于内存芯片级的单元寻址,与处理器和CPU连接的地址总线相对应。——这个概念应该是这几个概念中最好理解的一个,但是值得一提的是,虽然可以直接把物理地址理解成插在机器上那根内存本身,把内存看成一个从0字节一直到最大空量逐字节的编号的大数组,然后把这个数组叫做物理地址,但是事实上,这只是一个硬件提供给软件的抽像,内存的寻址方式并不是这样。所以,说它是“与地址总线相对应”,是更贴切一些,不过抛开对物理内存寻址方式的考虑,直接把物理地址与物理的内存一一对应,也是可以接受的。也许错误的理解更利于形而上的抽像。
相关概念可以参考:[逻辑地址、线性地址、物理地址和虚拟地址](http://www.cnblogs.com/diyingyun/archive/2012/01/03/2311327.html)
下面我们利用bochs说说操作系统的段页式转换:(本文程序在windows下进行)
关于Bochs的配置这里就不赘述,网上有很多教程!
首先在Bochs-2.1.1/linux-0.11-devel-040329下运行run.bat得到

内存调试窗口:
利用bochs将逻辑地址转化为物理地址理解操作系统的分段分页机制
程序运行窗口:
利用bochs将逻辑地址转化为物理地址理解操作系统的分段分页机制
(1)在内存调试窗口中输入c后
利用bochs将逻辑地址转化为物理地址理解操作系统的分段分页机制
利用bochs将逻辑地址转化为物理地址理解操作系统的分段分页机制

在程序运行窗口中写main.c文件,gcc –o main main.c编译 执行main
程序代码如下:

#include <stdio.h>

int main()
{
    static int a = 1;
    int b = 6;
    int c = 10;
    printf("&a:0x%x\n",&a);
    while(a);
    printf("Program exit!\n");

    return 0;
}

执行结果为:
利用bochs将逻辑地址转化为物理地址理解操作系统的分段分页机制
此时,程序阻塞到while(a);处。
我们的目标是将a的值修改为0,从而可以使程序运行通过。
(2)将内存调试窗口 ctrl+c跳出:
利用bochs将逻辑地址转化为物理地址理解操作系统的分段分页机制
(3)info cpu(dump_cpu)查看cpu寄存器内容
利用bochs将逻辑地址转化为物理地址理解操作系统的分段分页机制

变量a的值存在数据段,因此通过cpu的数据段寄存器ds的当前值作为段式映射的“选择码”,也就是用它作为段描述表中下标。
ds:s=0x17 转化为二进制:0000 0000 0001 0111
我们可以看出index = 2,TI = 1(LDT 局部描述符表),RPL = 3
ldtr:0x68 转化为二进制:0000 0000 0110 1000
同上 index = 13,TI = 0(GDT 全局描述符表),RPL = 0
gdtr:base=0x5cb8(段基址)
(4)xp /2wx 0x5cb8+13*8通过段基址+下标找出LDT(局部描述符表)的地址
利用bochs将逻辑地址转化为物理地址理解操作系统的分段分页机制
段描述符表的结构图如下:
利用bochs将逻辑地址转化为物理地址理解操作系统的分段分页机制
根据段描述符表的结构分析可知LDT的入口地址是:0x00fad2d0
(5)xp /2wx 0x00fad2d0+2*8 通过段基址+偏移量得到线性地址
利用bochs将逻辑地址转化为物理地址理解操作系统的分段分页机制
根据段描述符表的结构得到线性地址的基地址0x10000000,而a的地址0x3004,因此可得到a的线性地址为:0x10003004
calc ds:0x3004查看a的实际线性地址:
利用bochs将逻辑地址转化为物理地址理解操作系统的分段分页机制
由此可确认a的线性地址确实为:0x10003004
至此,分段结束。
(6)分页开始,分页机制如下图所示:
利用bochs将逻辑地址转化为物理地址理解操作系统的分段分页机制
将线性地址0x10003004,二进制为0001 0000 0000 0000 0011 0000 0000 0100
按10、10、12位划分, 0001000000 0000000011 000000000100可得
Director = 64 Table = 3 Offset = 4
首先查看cr0:0x80000001b最高位为1,说明支持分页机制;
再查看cr3: 0x0
xp /wx 0x0+64*4根据cr3和Director得到页目录索引:
利用bochs将逻辑地址转化为物理地址理解操作系统的分段分页机制
页目录索引的有效位为高20位,即0x00faa000,与Table可得到页表索引:
利用bochs将逻辑地址转化为物理地址理解操作系统的分段分页机制
页表索引的有效位也是高20位,即0x00fa9000,与Offset可得到实际的物理地址:
利用bochs将逻辑地址转化为物理地址理解操作系统的分段分页机制
即a的物理地址为0x00fa9004

(7)最后setpmem 0x00fa9004 4 0x0将a的值修改为0,使得程序可以通过。
利用bochs将逻辑地址转化为物理地址理解操作系统的分段分页机制
通过xp /wx 0x00fa9004查看a的值已被修改为0
(8)c 继续执行发现程序运行窗口的程序结束:
利用bochs将逻辑地址转化为物理地址理解操作系统的分段分页机制
完!!!

顺便再说一下逻辑地址、线性地址、虚拟地址、物理地址的区别和联系
就针对本例子来说:
逻辑地址:0x3004
线性地址(虚拟地址):0x10003004
物理地址:0x00fa9004

未分段前,逻辑地址=线性地址=虚拟地址,通过MMU分页后转化为物理地址
Linux下,逻辑地址与线性地址保持一致,因为所有的线性地址都是从0x00000000开始的。
线性地址=0x00000000(相当于基址)+逻辑地址(相当于偏移量)
如果没有分页机制,物理地址=逻辑地址

这下真的说完了,如有偏颇,希望同仁批评指正,谢谢!