地址映射与共享

 地址映射与共享

3.1 实验目的

深入理解操作系统的段、页式内存管理,深入理解段表、页表、逻辑地址、线性地址、物理地址等概念;

实践段、页式内存管理的地址映射过程;

3.2 实验内容

本次实验的基本内容是:

用Bochs调试工具跟踪Linux 0.11的地址翻译(地址映射)过程,了解IA-32和Linux 0.11的内存管理机制;

3.2.1 跟踪地址翻译过程

首先以汇编级调试的方式启动bochs,引导Linux 0.11,在0.11下编译和运行test.c。它是一个无限循环的程序,永远不会主动退出。然后在调试器中通过查看各项系统参数,从逻辑地址、LDT表、GDT表、线性地址到页表,计算出变量i的物理地址。最后通过直接修改物理内存的方式让test.c退出运行。test.c的代码如下:

#include <stdio.h>

int i = 0x12345678;

int main(void)

{    

printf("The logical/virtual address of i is 0x%08x", &i);    

fflush(stdout);    

while (i)       //注意是字母i不是数字1 

;    

return 0;

}

3.3 思考题

完成实验后,在实验报告中回答如下问题:

1.对于地址映射实验部分,列出你认为最重要的那几步(不超过4步),并给出你获得的实验数据。

2.test.c退出后,如果马上再运行一次,并再进行地址跟踪,你发现有哪些异同?为什么?

3.4 实验提示

《注释》中的5.3节和第13章对Linux 0.11的内存管理有详细分析、讲解,很值得一看。

3.4.1 IA-32的地址翻译过程

Linux 0.11完全遵循IA-32(Intel Architecture 32-bit)架构进行地址翻译,Windows、后续版本的Linux以及一切在IA-32保护模式下运行的操作系统都遵循此架构。因为只有这样才能充分发挥CPU的MMU的功能。关于此地址翻译过程的细节,请参考《注释》一书中的2.3.1-2.3.3节。

3.4.2 用Bochs汇编级调试功能进行人工地址翻译

此过程比较机械,基本不消耗脑细胞,做一下有很多好处。

3.4.2.1 准备

编译好Linux 0.11后,首先通过运行dbg-asm.bat启动调试器,此时Bochs的窗口处于黑屏状态,而命令行窗口显示:

======================================================================                       Bochs x86 Emulator 2.3.7

Build from CVS snapshot, on June 3, 2008

======================================================================00000000000i[     ] reading configuration from ./bochs/bochsrc.bxrc

00000000000i[     ] installing x module as the Bochs GUI

00000000000i[     ] using log file ./bochsout.txt

Next at t=0

(0) [0xfffffff0] f000:fff0 (unk. ctxt): jmp far f000:e05b; ea5be000f0

<bochs:1>_

“Next at t=0”表示下面的指令是Bochs启动后要执行的第一条软件指令。单步跟踪进去就能看到bios的代码。不过这不是本实验需要的。直接输入命令“c”,continue程序的运行,Bochs一如既往地启动了Linux 0.11。

在Linux 0.11下输入(或拷入)test.c,编译为test,运行之,打印如下信息:

The logical/virtual address of i is 0x00003004

只要test不变,0x00003004这个值在任何人的机器上都是一样的。即使在同一个机器上多次运行test,也是一样的。

test是一个死循环,只会不停占用CPU,不会退出。

3.4.2.2暂停

当test运行的时候,在命令行窗口按“ctrl+c”,Bochs会暂停运行,进入调试状态。绝大多数情况下都会停在test内,显示类似如下信息:

(0) [0x00fc8031] 000f:00000031 (unk. ctxt): cmp dword ptr ds:0x3004, 0x00000000 ; 833d0430000000

其中加粗的“000f”如果是“0008”,则说明中断在了内核里。那么就要c,然后再ctrl+c,直到变为“000f”为止。如果显示的下一条指令不是“cmp ...”,就用“n”命令单步运行几步,直到停在“cmp ...”。

使用命令“u /7”,显示从当前位置开始7条指令的反汇编代码,如下:

10000031: (                    ): cmp dword ptr ds:0x3004, 0x00000000 ; 833d0430000000

10000038: (                    ): jz .+0x00000002                   ; 7402

1000003a: (                    ): jmp .+0xfffffff5                    ; ebf5

1000003c: (                    ): xor eax, eax                       ; 31c0

1000003e: (                    ): jmp .+0x00000000                 ; eb00

10000040: (                    ): leave                            ; c9

10000041: (                    ): ret                              ; c3

这就是test.c中从while开始一直到return的汇编代码。变量i保存在ds:0x3004这个地址,并不停地和0进行比较,直到它为0,才会跳出循环。

现在,开始寻找ds:0x3004对应的物理地址。

3.4.2.3 段表

ds:0x3004是虚拟地址,ds表明这个地址属于ds段。首先要找到段表,然后通过ds的值在段表中找到ds段的具体信息,才能继续进行地址翻译。每个在IA-32上运行的应用程序都有一个段表,叫LDT(本地描述符表),段的信息叫段描述符。

LDT在哪里呢?ldtr寄存器是线索的起点,通过它可以在GDT(全局描述符表)中找到LDT的物理地址。

用“sreg”命令:

cs:s=0x000f, dl=0x00000002, dh=0x10c0fa00, valid=1

ds:s=0x0017, dl=0x00003fff, dh=0x10c0f300, valid=3

ss:s=0x0017, dl=0x00003fff, dh=0x10c0f300, valid=1

es:s=0x0017, dl=0x00003fff, dh=0x10c0f300, valid=1

fs:s=0x0017, dl=0x00003fff, dh=0x10c0f300, valid=1

gs:s=0x0017, dl=0x00003fff, dh=0x10c0f300, valid=1

ldtr:s=0x0068, dl=0xc2d00068, dh=0x000082f9, valid=1

tr:s=0x0060, dl=0x52e80068, dh=0x00008bfd, valid=1

gdtr:base=0x00005cc8, limit=0x7ff

idtr:base=0x000054c8, limit=0x7ff

可以看到ldtr的值是0x0068=0000000001101000(二进制),表示LDT表存放在GDT表的1101(二进制)=13(十进制)号位置(每位数据的意义参考后文叙述的段选择子)。而GDT的位置已经由gdtr明确给出,在物理地址的0x00005cc8。用“xp /32w 0x00005cc8”查看从该地址开始,32个字的内容,及GDT表的前16项,如下:

0x00005cc8 : 0x00000000 0x00000000 0x00000fff 0x00c09a00

0x00005cd8 : 0x00000fff 0x00c09300 0x00000000 0x00000000

0x00005ce8 : 0xa4280068 0x00008901 0xa4100068 0x00008201

0x00005cf8 : 0xf2e80068 0x000089ff 0xf2d00068 0x000082ff

0x00005d08 : 0xd2e80068 0x000089ff 0xd2d00068 0x000082ff

0x00005d18 : 0x12e80068 0x000089fc 0x12d00068 0x000082fc

0x00005d28 : 0xc2e80068 0x00008bf9 0xc2d00068 0x000082f9

0x00005d38 : 0x00000000 0x00000000 0x00000000 0x00000000

GDT表中的每一项占64位(8个字节),所以我们要查找的项的地址是“0x00005cc8 + 13 * 8”。“xp /2w 0x00005cc8 + 13 * 8”,得到:

0x00005d30 : 0xc2d00068 0x000082f9

上两步看到的数值可能和这里给出的示例不一致,这是很正常的。如果想确认是否准确,就看sreg输出中,ldtr所在行里,dl和dh的值,它们是Bochs的调试器自动计算出的,你寻找到的必须和它们一致。

“0xc2d00068 0x000082f9”将其中的加粗数字组合为“0x00f9c2d0”,这就是LDT表的物理地址(为什么这么组合,参考后文介绍的段描述符)。“xp /8w 0x00f9c2d0”,得到:

0x00f9c2d0 : 0x00000000 0x00000000 0x00000002 0x10c0fa00

0x00f9c2e0 : 0x00003fff 0x10c0f300 0x00000000 0x00f9d000

这就是LDT表的前4项内容了。

发布了7 篇原创文章 · 获赞 0 · 访问量 101

猜你喜欢

转载自blog.csdn.net/weixin_45969832/article/details/105682519