本实验主要是熟悉汇编、系统启动过程等。
环境配置参考:https://github.com/woai3c/MIT6.828/blob/master/docs/install.md
本实验代码:https://github.com/epis2048/MIT_6.828_2018/tree/lab1
一、系统启动
本部分不需要动手,只需要动脑了解内核是怎么运行的。
The PC’s Physical Address Space
物理地址空间示意:
1 | +------------------+ <- 0xFFFFFFFF (4GB) |
现代系统已经扩展到了4GB,BIOS放在最上面。
1MB以下空间是为了与过去兼容,现代计算机已经不用了。
二、引导加载器
初始化完成BIOS 之后,就运行Boot Loader,就是引导操作系统。引导系统主要两件事:
- 把实模式转换为保护模式。
- 通过x86的特殊I / O指令直接访问IDE磁盘设备寄存器,从而从硬盘读取内核。
1 | // boot/boot.S |
1 | // boot/main.c |
三、内核
1. 开启分页模式
操作系统经常被加载到高虚拟地址处,比如0xf0100000,但是并不是所有机器都有这么大的物理内存。可以使用内存管理硬件做到将高地址虚拟地址映射到低地址物理内存。
虚拟地址的高10位(0000000010B)作为页目录的下标,从页目录中获取页表的物理地址0x08001000,虚拟地址的第11~20位(0000000001B)作为页表的下标,得到该页对应的物理地址0x0000c000,最后将虚拟地址的低12位(000001010000B或者0x50)和得到的页的物理地址(0x0000c000)加得到0x00000c050就是虚拟地址0x00801050转换后的物理地址。
系统将页目录的物理地址复制到cr3寄存器,并且将cr0 的最高位PG位设置为1后,正式打开分页功能。
2. 格式化输出到控制的台
往控制台写字符串,本质是往物理地址0xB8000开始的显存写数据。
Exercise 8
要求添加一些代码,使能支持”%o”输出八进制。在vprintfmt()中找到case ‘o’的地方:
1
2
3
4
5
6
7 // lib/printfmt.c
// (unsigned) octal
case 'o':
num = getuint(&ap, lflag);
//设置基数为8
base = 8;
goto number;
3. 栈
函数调用过程:
- 执行call指令前,函数调用者将参数入栈,按照函数列表从右到左的顺序入栈
- call指令会自动将当前eip入栈,ret指令将自动从栈中弹出该值到eip寄存器
- 被调用函数负责:将ebp入栈,esp的值赋给ebp。
Exercise 11-12
补全mon_backtrace()函数,本质上就算根据ebp一层层网上找。
实验提供了int debuginfo_eip(uintptr_t addr, struct Eipdebuginfo *info)函数(在/kern/kdebug.c中),该函数输入eip,和一个Eipdebuginfo结构指针,执行完毕后,会将eip对应的信息填充到该结构中。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 // kern/monitor.c
int
mon_backtrace(int argc, char **argv, struct Trapframe *tf)
{
// Your code here.
uint32_t *ebp = (uint32_t *)read_ebp();
struct Eipdebuginfo eipDebugInfo;
while(ebp != 0){
uint32_t eip = *(ebp + 1);
//打印ebp,eip,最近的五个参数
cprintf("ebp %08x eip %08x args %08x %08x %08x %08x %08x\n", ebp, eip, *(ebp + 2), *(ebp + 3), *(ebp + 4), *(ebp + 5), *(ebp + 6));
//打印文件名等
debuginfo_eip((uintptr_t)eip, &eipDebugInfo);
//cprintf("%d", eipDebugInfo.eip_fn_namelen);
cprintf("\t%s:%d: %.*s+%d\n", eipDebugInfo.eip_file, eipDebugInfo.eip_line, eipDebugInfo.eip_fn_namelen, eipDebugInfo.eip_fn_name, eip - eipDebugInfo.eip_fn_addr);
//cprintf(": %.*s+%d\n", eipdebuginfo.eip_fn_namelen, eipdebuginfo.eip_fn_name, eipdebuginfo.eip_fn_addr);
//更新ebp
ebp = (uint32_t *)(*ebp);
}
return 0;
}