linux0.11—内存管理实验

实验基本内容

  • 用 Bochs 调试工具跟踪 Linux 0.11 的地址翻译(地址映射)过程,了解 IA-32 和 Linux 0.11 的内存管理机制;
  • 在 Ubuntu 上编写多进程的生产者—消费者程序,用共享内存做缓冲区;
  • 在信号量实验的基础上,为 Linux 0.11 增加共享内存功能,并将生产者—消费者程序移植到 Linux 0.11。

实验一

过程:

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

目的:

主要理解从逻辑地址到线性地址,再到物理地址的过程中,GDT、LDT表以及相关页表是怎么进行查找的过程。

编译Linux0.11

进入实验楼环境
cd oslab/
tar -zxvf hit... -C /home/shiyanlou

cd linux-0.11

linux-0.11目录中是linux的源代码,然后执行make all得到对应的Image,生成的目标文件是一个软盘镜像文件—— linux-0.11/Image。

到这里linux-0.11编译完成。

退回到oslab目录,执行./dbg-asm启动调试器

cd /home/shiyanlou/oslab
./dbg-asm

Bochs窗口处于黑屏状态

命令行窗口如下:

Next at t=0表示下面的指令是Bochs启动后要执行的第一条软件指令。这里单步跟踪进去就可以看到BIOS代码。

直接输入命令c,continue程序运行,启动linux0.11。

在Bochs界面编写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)
        ;
    return 0;
}

同时执行

gcc -o test test.c

得到如下:

image-20211017215812563

执行test程序:

image-20211017220117689

在命令行窗口按Ctrl+c,Bochs暂停运行,进入调试状态

注意其中红色框,因为第四个红框中没有出现cmp指令,因此使用n命令单步运行,如下

到这样即可。

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

<bochs:4> u /8
10000063: (                    ): cmp dword ptr ds:0x3004, 0x00000000 ; 833d0430000000
1000006a: (                    ): jz .+0x00000004           ; 7404
1000006c: (                    ): jmp .+0xfffffff5          ; ebf5
1000006e: (                    ): add byte ptr ds:[eax], al ; 0000
10000070: (                    ): xor eax, eax              ; 31c0
10000072: (                    ): jmp .+0x00000000          ; eb00
10000074: (                    ): leave                     ; c9
10000075: (                    ): ret                       ; c3

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

为了让linux-0.11中运行的test跳出循环,需要找到逻辑地址ds:0x3004对应的物理地址,将其内容(变量i)改为0。

寻找ds:0x3004对应的线性地址

ds:0x3004是虚拟地址,ds表明这个地址属于ds段。

首先要找到段表LDT,然后通过ds的值在段表中找到ds段的具体信息,才能继续进行地址翻译。

LDT的段描述符

段描述符放在LDT中,而LDT的描述符放在GDT中,GDT的地址与LDT在GDT中的项的索引分别保存在gdtr和ldtr中。

image-20211017222923469

可以看到ldtr的值是0x0068=0000 0000 0110 1000(二进制),根据段选择子的结构看

image-20211017223101040

可以知道索引为1101(二进制),即为13,TI位为0,表示GDT中的第13项为LDT的段描述符,每个段描述符64bit—>8byte。

由于GDT的地址已经由gdtr给出,在物理地址的0x00005cb8,那么用xp /32w 0x00005cb8查看从该地址开始,32个字内容,及GDT表的前16项:

image-20211017223603450

那么我们要查找的项的地址是0x00005cb8+ 13*8。

image-20211017223703479

得到的LDT段描述符(与sreg指令得到的ldtr中的dl、dh相同),从而我们可以得到LDT的基址为0x00faa2d0。

上面的LDT的基址是根据段描述符的结构得来的:

image-20211017223824711

上图中0xa2d00068是低32位,0x000082fa是高32位,从段描述符结构看,低32位的16到31位是基址的0-15位,然后高32位的0-7和24-31位是基址的16-31位,所以结果是LDT的基址为0x00faa2d0。

ds的段描述符和段基址

LDT表的前4项

<bochs:7> xp /8w 0x00faa2d0
[bochs]:
0x00faa2d0 <bogus+       0>:    0x00000000    0x00000000    0x00000002    0x10c0fa00
0x00faa2e0 <bogus+      16>:    0x00003fff    0x10c0f300    0x00000000    0x00fab000

ds段选择子为0x0017 => 0000 0000 0001 0111(二进制),可知索引为10(二进制)即2,TI位为1,即LDT中的第2项为ds的段描述符,每个段描述符64bit => 8byte。这里的TI位为1,所以去LDT表中查找。

从索引为2可知,ds的段描述符在LDT表的第3个,每个段描述符64bit,因此ds的段描述符为:

 0x00003fff    0x10c0f300

当然可以使用xp /2w 0x00faa2d0+2*8

image-20211017224907259

同样根据段描述符的结构可得段基址为0x10000000。

ds:0x3004的线性地址

由于线性地址=段基址+段内偏移,所以ds:0x3004的线性地址为0x10000000+0x3004=0x10003004。

用calc ds:3004验证:

image-20211017225211902

寻址ds:0x3004的物理地址

从线性地址到物理地址需要查找页表,页表工作原理如下:

image-20211017225337094

首先需要算出线性地址中的页目录号、页表号和页内偏移,分别对应32位线性地址的10位+10位+12位,所以0x10003004的页目录号为64,页号为3,页内偏移为4。

页目录表的位置由CR3寄存器指引。creg命令可以看到:

image-20211017225553296

所以页目录表的基址为0。

页目录表和页表中的内容很简单,是 1024 个 32 位(正好是 4K)数。这 32 位中前 20 位是物理页框号,后面是一些属性信息(其中最重要的是最后一位 P)。其中第 65 个页目录项就是我们要找的内容,用“xp /w 0+64*4”查看:

image-20211017225716863

其中027为属性。页表所在物理页框号为0x00fa5,即页表在物理内存的0x00fa5000位置。从该位置开始查找3号页表项,得到(xp /w 0x00fa5000+3*4)

image-20211017225925919

其中067是属性,那么线性地址0x10003004对应的物理页框号为0x00fa3,和页内偏移0x004接到一起就是0x00fa3004。

使用命令xp /w 0x00fa3004,进行验证,如下:

image-20211017230205487

可以看到这个数值确实是test.c中i的初始值。

说明0x00fa3004是对应物理地址。

修改对应物理地址中的值完成实验

现在,通过直接修改内存来改变 i 的值为 0,命令是: setpmem 0x00fa3004 4 0,表示从 0x00fa3004 地址开始的 4 个字节都设为 0。然后再用“c”命令继续 Bochs 的运行,可以看到 test 退出了,说明 i 的修改成功了,此项实验结束。

image-20211017230420025

猜你喜欢

转载自blog.csdn.net/kuangd_1992/article/details/120818048