lab 1
-
At what point does the processor start executing 32-bit code? What exactly causes the switch from 16- to 32-bit mode?
当 CR0 中的 PE 位被置为 1 时进入 30-bit protected mode
-
What is the last instruction of the boot loader executed, and what is the first instruction of the kernel it just loaded?
Last instruction of boot loader:
((void (*)(void)) (ELFHDR->e_entry))();
First instruction of kernel:
movl %cr4, %eax
-
How does the boot loader decide how many sectors it must read in order to fetch the entire kernel from disk? Where does it find this information?
boot/main.c 中,从 ELFHDR 中获得 sector 的数目并做相应循环。
-
Exercise 5
Before boot loader:
before.pngAfter boot loader:
after.pngdump 出obj/kern/kernel 文件:
objdump 结果
可以发现 kernel 的 .text 段被加载到了 0x00100000 处。
Part 3: The kernel
- kernel 被加载到 0xf0100000 的高地址(虚地址,kernel 认为自己被加载到),而实际上可能没有那么多物理地址,所以要做映射。事实上,kernel 的代码段被加载到 0x00100000(1MB)的地方。在本 lab 中,
kern/entrypgdir.c
手动地将虚拟地址0xf0000000~0xf0400000
和0x00000000~0x004000000
都映射到物理地址0x00000000~0x00400000
。
在整个 JOS 的实现中,将实现物理地址的0x00000000~0x0fffffff
到虚拟地址的0xf0000000~0xffffffff
(共 256M,JOS 只能使用物理地址地前 256M)。
- Exercise 7.
- Use QEMU and GDB to trace into the JOS kernel and find where the new virtual-to-physical mapping takes effect. Then examine the Global Descriptor Table (GDT) that the code uses to achieve this effect, and make sure you understand what's going on.
- What is the first instruction after the new mapping is established that would fail to work properly if the old mapping were still in place? Comment out or otherwise intentionally break the segmentation setup code in kern/entry.S, trace into it, and see if you were right.
Formatted Printing to the console
- Exercise 8
We have omitted a small fragment of code - the code necessary to print octal numbers using patterns of the form "%o". Find and fill in this code fragment. Remember the octal number should begin with '0'.
case 'o':
num = getuint(&ap, lflag);
putch('0', putdat);
base = 8;
goto number;
其中 putch() 函数地功能是向终端输出一个 char 并将一个 counter 加一。模仿 case 'u' ,并依照要求提前输出一个 '0' 。
- Exercise 9
You need also to add support for the "+" flag, which forces to precede the result with a plus or minus sign (+ or -) even for positive numbers.
// (signed) decimal
case 'd':
num = getint(&ap, lflag);
if ((long long) num < 0) {
putch('-', putdat);
num = -(long long) num;
}else if (plus){
putch('+', putdat);
}
base = 10;
goto number;
主要修改了 case 'd'
代码段,因为只有 "%d" 涉及正负号。负的部分已经写好,plus
为一个记录正号出现的 flag,当 reswitch 的前一个循环读到了 '+' 符号,则把 plus
置为 1。
-
Explain the interface between
printf.c
andconsole.c
. Specifically, what function doesconsole.c
export? How is this function used byprintf.c
?
printf.c
calls functioncputchar(int c)
fromconsole.c
, andconsole.c
calls functioncprintf(const char *fmt, ...)
defined byprintf.c
. -
Explain the following from
console.c
:if (crt_pos >= CRT_SIZE) { int i; memmove(crt_buf, crt_buf + CRT_COLS, (CRT_SIZE - CRT_COLS) * sizeof(uint16_t)); for (i = CRT_SIZE - CRT_COLS; i < CRT_SIZE; i++) crt_buf[i] = 0x0700 | ' '; crt_pos -= CRT_COLS; }
如果屏幕满了则现有显示内容向上滚动一行。
-
Run the following code.
unsigned int i = 0x00646c72; cprintf("H%x Wo%s", 57616, &i);
What is the output?
输出:He110 World
-
In the following code, what is going to be printed after 'y='? (note: the answer is not a specific value.) Why does this happen?
答案不定,由于传参依靠的是 stack ,取当时 stack 上参数3
再向上 4Byte 的值。 -
Exercise 10
写一个辅助函数printnumn
,这个函数做的事情大体和原来的printnum
相同:递归地打印 num 的每一位,并在递归基处补足小数点后的 0(如果是小数的话)。但同时要记录一个 len 变量,表示打印了多少位。
原来的printnum
函数调用printnumn
函数获得已打印的位数,在对比 width 参数补足后面的 padding。 -
Exercise 11
Determine where the kernel initializes its stack, and exactly where in memory its stack is located. How does the kernel reserve space for its stack? And at which "end" of this reserved area is the stack pointer initialized to point to?
浏览 /obj/kern/kernel.asm,发现 bootstacktop 的值为0x0f110000bc -
Exercise 13\14
首先在monitor.c
中的 command list 中注册 backtrace 的指令。
接着在mon_backtrace
函数中写循环,循环的结束条件为 cur_ebp == 0,在每次循环中根据 cur_ebp 目前的值取得其他变量的值并格式化输出,每次循环结束时将 cur_ebp 做一次 dereference,并将新值赋给 cur_ebp 。
通过阅读kdebug.c
中的注释,补全缺的eip_line
部分,然后在mon_backtrace
函数中调用debuginfo_eip
函数获取信息并格式化输出。 -
Exercise 15
先在monitor.h
中加上mon_time
函数的声明,然后在 command list 中注册 time 的指令。
接着定义mon_time
函数,其中调用的辅助函数read_cpu_cycle
使用嵌入汇编并加上 volatile 关键字执行 rdtsc 指令获取周期数。
在两次调用mon_time
函数中间执行命令行键入的指令,然后两个结果相减输出。