完成了本实验的前两个内容,即跟踪地址映射过程和在Unbuntu上利用共享内存编写生产者消费者程序。
1.用Bochs 调试工具跟踪 Linux 0.11 的地址翻译:
该实验的步骤在实验楼中有极其详细的说明,所以完全参照那个步骤做即可。在这里我记录了一些需要注意的点,以及我的实验过程。
先对逻辑地址、虚拟地址、线性地址、物理地址这几个概念做一个区分(来自linux0.11注释152页):
逻辑地址:指由程序产生的与段相关的偏移地址。
虚拟地址:由段选择符和段内偏移地址两个部分组成的地址。(有些资料不区分逻辑地址和虚拟地址的概念,将它们统称为逻辑地址)
线性地址:根据段选择符查段表得到的段基址,加上偏移地址就得到线性地址。是虚拟地址到物理地址变换的中间层,是处理器可寻址的内存空间(称为线性地址空间)中的地址。
物理地址:是地址变换的最终结果地址,实际内存存储器上的地址。
实验步骤:
通过sudo ./mount-hdc挂载后,进入hdc/usr/root目录,在其中创建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;
}
接下来我们将用Bochs模拟器的调试功能,从逻辑地址开始,进行人工查找段表页表,翻译成最终的物理地址。Bochs调试的指令可以参考https://my.oschina.net/u/1777508/blog/1841996
1.得到逻辑地址:
编译好 Linux 0.11 后,首先通过运行 ./dbg-asm 启动调试器,此时 Bochs 的窗口处于黑屏状态。此时打开了两个窗口,一个是Bochs窗口,一个是命令行窗口(注意如果在实验楼的话,不要点最小化,否则就打不开了。正常窗口尺寸就行,也不要最大化,否则会挡住另一个窗口)。
在命令行输入c,Bochs便会继续运行,启动了linux0.11,然后在linux中编译test.c文件,然后打开。出现如下信息,并进入无限循环。
The logical/virtual address of i is 0x00003004
打印出了变量i的逻辑地址,这个地址在任何人的机器上都是一样的。
2.寻找虚拟地址:
此时在命令行中按Ctrl+c ,暂停Bochs的运行,进入调试状态。命令行中显示如下信息:
(0) [0x00fc8031] 000f:00000031 (unk. ctxt): cmp dword ptr ds:0x3004, 0x00000000 ; 833d0430000000
如上图一般是000f,如果显示的是0008说明中断在了内核中,那么就要 c,然后再 ctrl+c,直到变为 000f 为止。如上图一般显示的下一条指令是 cmp …(这里指语句以 cmp 开头),否则就用 n 命令单步运行几步,直到停在 cmp …。
上图代码已经显示出变量i的虚拟地址是ds:0x3004了(段选择符+逻辑地址),我们也可以用命令 u /8,显示从当前位置开始 8 条指令的反汇编代码,即从while开始的机器语言代码反汇编成汇编语言代码(如下图),可以看到变量 i 保存在 ds:0x3004 这个地址,并不停地和 0 进行比较,直到它为 0,才会跳出循环。因此接下来我们要寻找虚拟地址 ds:0x3004 对应的线性地址。
<bochs:3> 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
3.寻找线性地址:
刚才找到的虚拟地址中,段选择符是ds,所以我们需要找到段表,再查段表得到ds段的具体信息。应用程序的段表是LDT,所以我们首先要找到段表LDT的位置。
寻找LDT表:
GDT(全局描述符表,操作系统使用的段表)中保存着 LDT 的物理地址,而ldtr 寄存器中记录着LDT物理地址在GDT表中的位置。
在命令行窗口输入sreg命令后,显示出各寄存器的信息,如下图,可以看到ldtr的值是 0x0068(不同人的机器可能得到的值不一样,之后注意对应好位置就好),转换成二进制是0000000001101000,其中1101这四位表示LDT的物理地址在GDT表中的位置(各个位的意义可以参考linux0.11注释书或者实验楼中的提示),1101转换成十进制是13。
下图中gdtr的值是GDT表的物理地址,即0x00005cb8。
<bochs:4> 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