update for mooc OS labs

This commit is contained in:
yuchen 2015-01-30 20:20:30 +08:00
parent 6a4eb1f465
commit 87059df293
57 changed files with 586 additions and 129 deletions

Binary file not shown.

View File

@ -1,88 +0,0 @@
第二次作业
OS concept ver7课本第8章、第九章的习题
8.3 Given ?ve memory partitions of 100 KB, 500 KB, 200 KB, 300 KB,and
600 KB (in order), how would each of the ?rst-?t, best-?t, and worst-?t
algorithms place processes of 212 KB, 417 KB, 112 KB, and 426 KB (in
order)?Which algorithm makes the most ef?cient use of memory?
8.4 Most systems allow programs to allocate more memory to its address
space during execution.Data allocated in the heap segments of programs
is an example of such allocated memory. What is required to support
dynamic memory allocation in the following schemes:
a. contiguous-memory allocation
b. pure segmentation
c. pure paging
8.12 Consider the following segment table:
Segment Base Length
0 219 600
1 2300 14
2 90 100
3 1327 580
4 1952 96
What are the physical addresses for the following logical addresses?
a. 0,430
b. 1,10
c. 2,500
d. 3,400
e. 4,112
9.5 Assume we have a demand-paged memory. The page table is held in
registers. It takes 8 milliseconds to service a page fault if an empty page
is available or the replaced page is not modi?ed, and 20 milliseconds if
the replaced page is modi?ed. Memory access time is 100 nanoseconds.
Assume that the page to be replaced is modi?ed 70 percent of the time.
What is the maximum acceptable page-fault rate for an effective access
time of no more than 200 nanoseconds?
9.13 A page-replacement algorithm should minimize the number of page
faults. We can do this minimization by distributing heavily used pages
evenly over all of memory, rather than having them compete for a small
number of page frames.We can associatewith each page frame a counter
of the number of pages that are associated with that frame. Then, to
replace a page, we search for the page frame with the smallest counter.
a. De?ne a page-replacement algorithmusing this basic idea. Specif-
ically address the problems of (1) what the initial value of the
counters is, (2) when counters are increased, (3) when counters
are decreased, and (4) how the page to be replaced is selected.
b. Howmany page faults occur for your algorithmfor the following
reference string, for four page frames?
1, 2, 3, 4, 5, 3, 4, 1, 6, 7, 8, 7, 8, 9, 7, 8, 9, 5, 4, 5, 4, 2.
c. What is the minimumnumber of page faults for an optimal page-
replacement strategy for the reference string in part b with four
page frames?
1. 请证明LRU算法不会存在belady现象。
2. 请证明或详细说明FIFO/CLOCK/Enhanced CLOCK是否有belady现象
实验相关
------------------
1 lab1的proj1的ucore的代码中是否有使用了绝对地址编译期间指定的绝对内存地址
2 lab2 的ucore的小os的load addr和link addr分别是多少?
3 ucore用了哪个数据结构来管理空闲内存位于内存什么地方占多少空间
4 请考虑在ucore中实现second chance/enhanced clock页替换算法的设计思路。主要描述如何利用相关x86相关硬件、如何设计数据结构大致要实现哪些函数函数的大致功能和整体流程。
5 实现enhanced clock algorithm中
IF before clock sweep: (used,dirty) = (1,1), THEN after clock sweep: (used, dirty)=(0,1).
如果把上述转换改为:
IF before clock sweep: (used,dirty) = (1,1), THEN after clock sweep: (used, dirty)=(1,0).
是否可行?
6 在clock算法实现中pte中的uesd bit位是否可以让os来设置1 or 0 ? 为什么?
7 在enhanced clock算法中的dirty bit位是否可以让os来设置1 or 0 ? 为什么?
8 如果在ucore中实现精确的LRU算法如何设计需要硬件和OS分别完成什么事情
9 如果在ucore中实现工作集页替换算法和缺页频率替换算法如何设计

View File

@ -1,15 +0,0 @@
#第三次作业 process/thread
1. process与thread在实现上和执行上有何区别
2. process与program程序)的区别与联系是什么?
3. process的执行状态转换图是啥各个状态的含义是什么
4. thread的实现方式有几种这几种有何区别
5. fork和exec的执行逻辑大致要完成的事情是什么
6. 父进程与子进程之间在内容上有何差异?在执行上有何差异?
7. 父进程fork完子进程后这两个进程在执行的过程中是否有先后顺序
8. 进程/线程上下文切换的执行逻辑是什么?二者有无区别?如有,区别是啥?
9. 为何vfork机制在有了Copy on Write (简称COW)技术后意义变得不大了?
10. 如何在ucore中实现COW技术
11. 进程有几个栈?栈的作用是什么?
12. exec的内核实现中如何返回到新的进程的入口点并正确执行用户态的进程
13. 内核线程和用户进程的mm结构有何区别为什么
14. 如何在ucore中实现可在用户态执行的线程

View File

@ -207,6 +207,8 @@ TARGETS: $(TARGETS)
.DEFAULT_GOAL := TARGETS .DEFAULT_GOAL := TARGETS
.PHONY: qemu qemu-nox debug debug-nox .PHONY: qemu qemu-nox debug debug-nox
qemu-mon: $(UCOREIMG)
$(V)$(QEMU) -monitor stdio -hda $< -serial null
qemu: $(UCOREIMG) qemu: $(UCOREIMG)
$(V)$(QEMU) -parallel stdio -hda $< -serial null $(V)$(QEMU) -parallel stdio -hda $< -serial null

View File

@ -231,6 +231,8 @@ targets: $(TARGETS)
QEMUOPTS = -hda $(UCOREIMG) QEMUOPTS = -hda $(UCOREIMG)
.PHONY: qemu qemu-nox gdb debug debug-mon debug-nox .PHONY: qemu qemu-nox gdb debug debug-mon debug-nox
qemu-mon: targets
$(V)$(QEMU) -monitor stdio $(QEMUOPTS) -serial null
qemu: targets qemu: targets
$(V)$(QEMU) -parallel stdio $(QEMUOPTS) -serial null $(V)$(QEMU) -parallel stdio $(QEMUOPTS) -serial null

View File

@ -225,6 +225,8 @@ TARGETS: $(TARGETS)
QEMUOPTS = -hda $(UCOREIMG) -drive file=$(SWAPIMG),media=disk,cache=writeback QEMUOPTS = -hda $(UCOREIMG) -drive file=$(SWAPIMG),media=disk,cache=writeback
.PHONY: qemu qemu-nox debug debug-nox .PHONY: qemu qemu-nox debug debug-nox
qemu-mon: $(UCOREIMG) $(SWAPIMG)
$(V)$(QEMU) -monitor stdio $(QEMUOPTS) -serial null
qemu: $(UCOREIMG) $(SWAPIMG) qemu: $(UCOREIMG) $(SWAPIMG)
$(V)$(QEMU) -parallel stdio $(QEMUOPTS) -serial null $(V)$(QEMU) -parallel stdio $(QEMUOPTS) -serial null

View File

@ -229,6 +229,8 @@ TARGETS: $(TARGETS)
QEMUOPTS = -hda $(UCOREIMG) -drive file=$(SWAPIMG),media=disk,cache=writeback QEMUOPTS = -hda $(UCOREIMG) -drive file=$(SWAPIMG),media=disk,cache=writeback
.PHONY: qemu qemu-nox debug debug-nox .PHONY: qemu qemu-nox debug debug-nox
qemu-mon: $(UCOREIMG) $(SWAPIMG)
$(V)$(QEMU) -monitor stdio $(QEMUOPTS) -serial null
qemu: $(UCOREIMG) $(SWAPIMG) qemu: $(UCOREIMG) $(SWAPIMG)
$(V)$(QEMU) -parallel stdio $(QEMUOPTS) -serial null $(V)$(QEMU) -parallel stdio $(QEMUOPTS) -serial null

View File

@ -268,6 +268,8 @@ TARGETS: $(TARGETS)
QEMUOPTS = -hda $(UCOREIMG) -drive file=$(SWAPIMG),media=disk,cache=writeback QEMUOPTS = -hda $(UCOREIMG) -drive file=$(SWAPIMG),media=disk,cache=writeback
.PHONY: qemu qemu-nox debug debug-nox .PHONY: qemu qemu-nox debug debug-nox
qemu-mon: $(UCOREIMG) $(SWAPIMG)
$(V)$(QEMU) -monitor stdio $(QEMUOPTS) -serial null
qemu: $(UCOREIMG) $(SWAPIMG) qemu: $(UCOREIMG) $(SWAPIMG)
$(V)$(QEMU) -parallel stdio $(QEMUOPTS) -serial null $(V)$(QEMU) -parallel stdio $(QEMUOPTS) -serial null

View File

@ -268,6 +268,8 @@ TARGETS: $(TARGETS)
QEMUOPTS = -hda $(UCOREIMG) -drive file=$(SWAPIMG),media=disk,cache=writeback QEMUOPTS = -hda $(UCOREIMG) -drive file=$(SWAPIMG),media=disk,cache=writeback
.PHONY: qemu qemu-nox debug debug-nox .PHONY: qemu qemu-nox debug debug-nox
qemu-mon: $(UCOREIMG) $(SWAPIMG)
$(V)$(QEMU) -monitor stdio $(QEMUOPTS) -serial null
qemu: $(UCOREIMG) $(SWAPIMG) qemu: $(UCOREIMG) $(SWAPIMG)
$(V)$(QEMU) -parallel stdio $(QEMUOPTS) -serial null $(V)$(QEMU) -parallel stdio $(QEMUOPTS) -serial null

View File

@ -268,6 +268,8 @@ TARGETS: $(TARGETS)
QEMUOPTS = -hda $(UCOREIMG) -drive file=$(SWAPIMG),media=disk,cache=writeback QEMUOPTS = -hda $(UCOREIMG) -drive file=$(SWAPIMG),media=disk,cache=writeback
.PHONY: qemu qemu-nox debug debug-nox .PHONY: qemu qemu-nox debug debug-nox
qemu-mon: $(UCOREIMG) $(SWAPIMG)
$(V)$(QEMU) -monitor stdio $(QEMUOPTS) -serial null
qemu: $(UCOREIMG) $(SWAPIMG) qemu: $(UCOREIMG) $(SWAPIMG)
$(V)$(QEMU) -parallel stdio $(QEMUOPTS) -serial null $(V)$(QEMU) -parallel stdio $(QEMUOPTS) -serial null

View File

@ -308,6 +308,8 @@ TARGETS: $(TARGETS)
QEMUOPTS = -hda $(UCOREIMG) -drive file=$(SWAPIMG),media=disk,cache=writeback -drive file=$(SFSIMG),media=disk,cache=writeback QEMUOPTS = -hda $(UCOREIMG) -drive file=$(SWAPIMG),media=disk,cache=writeback -drive file=$(SFSIMG),media=disk,cache=writeback
.PHONY: qemu qemu-nox debug debug-nox monitor .PHONY: qemu qemu-nox debug debug-nox monitor
qemu-mon: $(UCOREIMG) $(SWAPIMG) $(SFSIMG)
$(V)$(QEMU) -monitor stdio $(QEMUOPTS) -serial null
qemu: $(UCOREIMG) $(SWAPIMG) $(SFSIMG) qemu: $(UCOREIMG) $(SWAPIMG) $(SFSIMG)
$(V)$(QEMU) -parallel stdio $(QEMUOPTS) -serial null $(V)$(QEMU) -parallel stdio $(QEMUOPTS) -serial null

View File

@ -23,8 +23,8 @@ endif
# try to infer the correct QEMU # try to infer the correct QEMU
ifndef QEMU ifndef QEMU
QEMU := $(shell if which qemu > /dev/null; \ QEMU := $(shell if which qemu-system-i386 > /dev/null; \
then echo 'qemu'; exit; \ then echo 'qemu-system-i386'; exit; \
elif which i386-elf-qemu > /dev/null; \ elif which i386-elf-qemu > /dev/null; \
then echo 'i386-elf-qemu'; exit; \ then echo 'i386-elf-qemu'; exit; \
else \ else \
@ -197,6 +197,17 @@ TARGETS: $(TARGETS)
.DEFAULT_GOAL := TARGETS .DEFAULT_GOAL := TARGETS
.PHONY: qemu qemu-nox debug debug-nox .PHONY: qemu qemu-nox debug debug-nox
lab1-mon: $(UCOREIMG)
$(V)$(TERMINAL) -e "$(QEMU) -S -s -d in_asm -D $(BINDIR)/q.log -monitor stdio -hda $< -serial null"
$(V)sleep 2
$(V)$(TERMINAL) -e "gdb -q -x tools/lab1init"
debug-mon: $(UCOREIMG)
# $(V)$(QEMU) -S -s -monitor stdio -hda $< -serial null &
$(V)$(TERMINAL) -e "$(QEMU) -S -s -monitor stdio -hda $< -serial null"
$(V)sleep 2
$(V)$(TERMINAL) -e "gdb -q -x tools/moninit"
qemu-mon: $(UCOREIMG)
$(V)$(QEMU) -monitor stdio -hda $< -serial null
qemu: $(UCOREIMG) qemu: $(UCOREIMG)
$(V)$(QEMU) -parallel stdio -hda $< -serial null $(V)$(QEMU) -parallel stdio -hda $< -serial null

View File

@ -27,20 +27,20 @@ start:
# address line 20 is tied low, so that addresses higher than # address line 20 is tied low, so that addresses higher than
# 1MB wrap around to zero by default. This code undoes this. # 1MB wrap around to zero by default. This code undoes this.
seta20.1: seta20.1:
inb $0x64, %al # Wait for not busy inb $0x64, %al # Wait for not busy(8042 input buffer empty).
testb $0x2, %al testb $0x2, %al
jnz seta20.1 jnz seta20.1
movb $0xd1, %al # 0xd1 -> port 0x64 movb $0xd1, %al # 0xd1 -> port 0x64
outb %al, $0x64 outb %al, $0x64 # 0xd1 means: write data to 8042's P2 port
seta20.2: seta20.2:
inb $0x64, %al # Wait for not busy inb $0x64, %al # Wait for not busy(8042 input buffer empty).
testb $0x2, %al testb $0x2, %al
jnz seta20.2 jnz seta20.2
movb $0xdf, %al # 0xdf -> port 0x60 movb $0xdf, %al # 0xdf -> port 0x60
outb %al, $0x60 outb %al, $0x60 # 0xdf = 11011111, means set P2's A20 bit(the 1 bit) to 1
# Switch from real to protected mode, using a bootstrap GDT # Switch from real to protected mode, using a bootstrap GDT
# and segment translation that makes virtual addresses # and segment translation that makes virtual addresses

View File

@ -8,9 +8,9 @@
#include <clock.h> #include <clock.h>
#include <intr.h> #include <intr.h>
#include <pmm.h> #include <pmm.h>
#include <kmonitor.h>
int kern_init(void) __attribute__((noreturn)); int kern_init(void) __attribute__((noreturn));
void grade_backtrace(void);
static void lab1_switch_test(void); static void lab1_switch_test(void);
int int
@ -37,7 +37,7 @@ kern_init(void) {
//LAB1: CAHLLENGE 1 If you try to do it, uncomment lab1_switch_test() //LAB1: CAHLLENGE 1 If you try to do it, uncomment lab1_switch_test()
// user/kernel mode switch test // user/kernel mode switch test
//lab1_switch_test(); lab1_switch_test();
/* do nothing */ /* do nothing */
while (1); while (1);
@ -84,11 +84,24 @@ lab1_print_cur_status(void) {
static void static void
lab1_switch_to_user(void) { lab1_switch_to_user(void) {
//LAB1 CHALLENGE 1 : TODO //LAB1 CHALLENGE 1 : TODO
asm volatile (
"sub $0x8, %%esp \n"
"int %0 \n"
"movl %%ebp, %%esp"
:
: "i"(T_SWITCH_TOU)
);
} }
static void static void
lab1_switch_to_kernel(void) { lab1_switch_to_kernel(void) {
//LAB1 CHALLENGE 1 : TODO //LAB1 CHALLENGE 1 : TODO
asm volatile (
"int %0 \n"
"movl %%ebp, %%esp \n"
:
: "i"(T_SWITCH_TOK)
);
} }
static void static void

View File

@ -8,7 +8,7 @@
#include <assert.h> #include <assert.h>
#include <console.h> #include <console.h>
#include <kdebug.h> #include <kdebug.h>
#include <string.h>
#define TICK_NUM 100 #define TICK_NUM 100
static void print_ticks() { static void print_ticks() {
@ -51,6 +51,9 @@ idt_init(void) {
for (i = 0; i < sizeof(idt) / sizeof(struct gatedesc); i ++) { for (i = 0; i < sizeof(idt) / sizeof(struct gatedesc); i ++) {
SETGATE(idt[i], 0, GD_KTEXT, __vectors[i], DPL_KERNEL); SETGATE(idt[i], 0, GD_KTEXT, __vectors[i], DPL_KERNEL);
} }
// set for switch from user to kernel
SETGATE(idt[T_SWITCH_TOK], 0, GD_KTEXT, __vectors[T_SWITCH_TOK], DPL_USER);
// load the IDT
lidt(&idt_pd); lidt(&idt_pd);
} }
@ -140,6 +143,9 @@ print_regs(struct pushregs *regs) {
cprintf(" eax 0x%08x\n", regs->reg_eax); cprintf(" eax 0x%08x\n", regs->reg_eax);
} }
/* temporary trapframe or pointer to trapframe */
struct trapframe switchk2u, *switchu2k;
/* trap_dispatch - dispatch based on what type of trap occurred */ /* trap_dispatch - dispatch based on what type of trap occurred */
static void static void
trap_dispatch(struct trapframe *tf) { trap_dispatch(struct trapframe *tf) {
@ -168,8 +174,30 @@ trap_dispatch(struct trapframe *tf) {
break; break;
//LAB1 CHALLENGE 1 : YOUR CODE you should modify below codes. //LAB1 CHALLENGE 1 : YOUR CODE you should modify below codes.
case T_SWITCH_TOU: case T_SWITCH_TOU:
if (tf->tf_cs != USER_CS) {
switchk2u = *tf;
switchk2u.tf_cs = USER_CS;
switchk2u.tf_ds = switchk2u.tf_es = switchk2u.tf_ss = USER_DS;
switchk2u.tf_esp = (uint32_t)tf + sizeof(struct trapframe) - 8;
// set eflags, make sure ucore can use io under user mode.
// if CPL > IOPL, then cpu will generate a general protection.
switchk2u.tf_eflags |= FL_IOPL_MASK;
// set temporary stack
// then iret will jump to the right stack
*((uint32_t *)tf - 1) = (uint32_t)&switchk2u;
}
break;
case T_SWITCH_TOK: case T_SWITCH_TOK:
panic("T_SWITCH_** ??\n"); if (tf->tf_cs != KERNEL_CS) {
tf->tf_cs = KERNEL_CS;
tf->tf_ds = tf->tf_es = KERNEL_DS;
tf->tf_eflags &= ~FL_IOPL_MASK;
switchu2k = (struct trapframe *)(tf->tf_esp - (sizeof(struct trapframe) - 8));
memmove(switchu2k, tf, sizeof(struct trapframe) - 8);
*((uint32_t *)tf - 1) = (uint32_t)switchu2k;
}
break; break;
case IRQ_OFFSET + IRQ_IDE1: case IRQ_OFFSET + IRQ_IDE1:
case IRQ_OFFSET + IRQ_IDE2: case IRQ_OFFSET + IRQ_IDE2:

View File

@ -0,0 +1,469 @@
Lab1 report
[练习1]
[练习1.1] 操作系统镜像文件 ucore.img 是如何一步一步生成的?(需要比较详细地解释 Makefile 中
每一条相关命令和命令参数的含义,以及说明命令导致的结果)
bin/ucore.img
| 生成ucore.img的相关代码为
| $(UCOREIMG): $(kernel) $(bootblock)
| $(V)dd if=/dev/zero of=$@ count=10000
| $(V)dd if=$(bootblock) of=$@ conv=notrunc
| $(V)dd if=$(kernel) of=$@ seek=1 conv=notrunc
|
| 为了生成ucore.img首先需要生成bootblock、kernel
|
|> bin/bootblock
| | 生成bootblock的相关代码为
| | $(bootblock): $(call toobj,$(bootfiles)) | $(call totarget,sign)
| | @echo + ld $@
| | $(V)$(LD) $(LDFLAGS) -N -e start -Ttext 0x7C00 $^ \
| | -o $(call toobj,bootblock)
| | @$(OBJDUMP) -S $(call objfile,bootblock) > \
| | $(call asmfile,bootblock)
| | @$(OBJCOPY) -S -O binary $(call objfile,bootblock) \
| | $(call outfile,bootblock)
| | @$(call totarget,sign) $(call outfile,bootblock) $(bootblock)
| |
| | 为了生成bootblock首先需要生成bootasm.o、bootmain.o、sign
| |
| |> obj/boot/bootasm.o, obj/boot/bootmain.o
| | | 生成bootasm.o,bootmain.o的相关makefile代码为
| | | bootfiles = $(call listf_cc,boot)
| | | $(foreach f,$(bootfiles),$(call cc_compile,$(f),$(CC),\
| | | $(CFLAGS) -Os -nostdinc))
| | | 实际代码由宏批量生成
| | |
| | | 生成bootasm.o需要bootasm.S
| | | 实际命令为
| | | gcc -Iboot/ -fno-builtin -Wall -ggdb -m32 -gstabs \
| | | -nostdinc -fno-stack-protector -Ilibs/ -Os -nostdinc \
| | | -c boot/bootasm.S -o obj/boot/bootasm.o
| | | 其中关键的参数为
| | | -ggdb 生成可供gdb使用的调试信息
| | | -m32 生成适用于32位环境的代码
| | | -gstabs 生成stabs格式的调试信息
| | | -nostdinc 不使用标准库
| | | -fno-stack-protector 不生成用于检测缓冲区溢出的代码
| | | -Os 为减小代码大小而进行优化
| | | -I<dir> 添加搜索头文件的路径
| | |
| | | 生成bootmain.o需要bootmain.c
| | | 实际命令为
| | | gcc -Iboot/ -fno-builtin -Wall -ggdb -m32 -gstabs -nostdinc \
| | | -fno-stack-protector -Ilibs/ -Os -nostdinc \
| | | -c boot/bootmain.c -o obj/boot/bootmain.o
| | | 新出现的关键参数有
| | | -fno-builtin 除非用__builtin_前缀
| | | 否则不进行builtin函数的优化
| |
| |> bin/sign
| | | 生成sign工具的makefile代码为
| | | $(call add_files_host,tools/sign.c,sign,sign)
| | | $(call create_target_host,sign,sign)
| | |
| | | 实际命令为
| | | gcc -Itools/ -g -Wall -O2 -c tools/sign.c \
| | | -o obj/sign/tools/sign.o
| | | gcc -g -Wall -O2 obj/sign/tools/sign.o -o bin/sign
| |
| | 首先生成bootblock.o
| | ld -m elf_i386 -nostdlib -N -e start -Ttext 0x7C00 \
| | obj/boot/bootasm.o obj/boot/bootmain.o -o obj/bootblock.o
| | 其中关键的参数为
| | -m <emulation> 模拟为i386上的连接器
| | -nostdlib 不使用标准库
| | -N 设置代码段和数据段均可读写
| | -e <entry> 指定入口
| | -Ttext 制定代码段开始位置
| |
| | 拷贝二进制代码bootblock.o到bootblock.out
| | objcopy -S -O binary obj/bootblock.o obj/bootblock.out
| | 其中关键的参数为
| | -S 移除所有符号和重定位信息
| | -O <bfdname> 指定输出格式
| |
| | 使用sign工具处理bootblock.out生成bootblock
| | bin/sign obj/bootblock.out bin/bootblock
|
|> bin/kernel
| | 生成kernel的相关代码为
| | $(kernel): tools/kernel.ld
| | $(kernel): $(KOBJS)
| | @echo + ld $@
| | $(V)$(LD) $(LDFLAGS) -T tools/kernel.ld -o $@ $(KOBJS)
| | @$(OBJDUMP) -S $@ > $(call asmfile,kernel)
| | @$(OBJDUMP) -t $@ | $(SED) '1,/SYMBOL TABLE/d; s/ .* / /; \
| | /^$$/d' > $(call symfile,kernel)
| |
| | 为了生成kernel首先需要 kernel.ld init.o readline.o stdio.o kdebug.o
| | kmonitor.o panic.o clock.o console.o intr.o picirq.o trap.o
| | trapentry.o vectors.o pmm.o printfmt.o string.o
| | kernel.ld已存在
| |
| |> obj/kern/*/*.o
| | | 生成这些.o文件的相关makefile代码为
| | | $(call add_files_cc,$(call listf_cc,$(KSRCDIR)),kernel,\
| | | $(KCFLAGS))
| | | 这些.o生成方式和参数均类似仅举init.o为例其余不赘述
| |> obj/kern/init/init.o
| | | 编译需要init.c
| | | 实际命令为
| | | gcc -Ikern/init/ -fno-builtin -Wall -ggdb -m32 \
| | | -gstabs -nostdinc -fno-stack-protector \
| | | -Ilibs/ -Ikern/debug/ -Ikern/driver/ \
| | | -Ikern/trap/ -Ikern/mm/ -c kern/init/init.c \
| | | -o obj/kern/init/init.o
| |
| | 生成kernel时makefile的几条指令中有@前缀的都不必需
| | 必需的命令只有
| | ld -m elf_i386 -nostdlib -T tools/kernel.ld -o bin/kernel \
| | obj/kern/init/init.o obj/kern/libs/readline.o \
| | obj/kern/libs/stdio.o obj/kern/debug/kdebug.o \
| | obj/kern/debug/kmonitor.o obj/kern/debug/panic.o \
| | obj/kern/driver/clock.o obj/kern/driver/console.o \
| | obj/kern/driver/intr.o obj/kern/driver/picirq.o \
| | obj/kern/trap/trap.o obj/kern/trap/trapentry.o \
| | obj/kern/trap/vectors.o obj/kern/mm/pmm.o \
| | obj/libs/printfmt.o obj/libs/string.o
| | 其中新出现的关键参数为
| | -T <scriptfile> 让连接器使用指定的脚本
|
| 生成一个有10000个块的文件每个块默认512字节用0填充
| dd if=/dev/zero of=bin/ucore.img count=10000
|
| 把bootblock中的内容写到第一个块
| dd if=bin/bootblock of=bin/ucore.img conv=notrunc
|
| 从第二个块开始写kernel中的内容
| dd if=bin/kernel of=bin/ucore.img seek=1 conv=notrunc
[练习1.2] 一个被系统认为是符合规范的硬盘主引导扇区的特征是什么?
从sign.c的代码来看一个磁盘主引导扇区只有512字节。且
第510个倒数第二个字节是0x55
第511个倒数第一个字节是0xAA。
[练习2]
[练习2.1] 从 CPU 加电后执行的第一条指令开始,单步跟踪 BIOS 的执行。
通过改写Makefile文件 ()
debug: $(UCOREIMG)
$(V)$(TERMINAL) -e "$(QEMU) -S -s -d in_asm -D $(BINDIR)/q.log -parallel stdio -hda $< -serial null"
$(V)sleep 2
$(V)$(TERMINAL) -e "gdb -q -tui -x tools/gdbinit"
在调用qemu时增加-d in_asm -D q.log参数便可以将运行的汇编指令保存在q.log中。
为防止qemu在gdb连接后立即开始执行删除了tools/gdbinit中的"continue"行。
[练习2.2] 在初始化位置0x7c00 设置实地址断点,测试断点正常。
在tools/gdbinit结尾加上
set architecture i8086 //设置当前调试的CPU是8086
b *0x7c00 //在0x7c00处设置断点。此地址是bootloader入口点地址可看boot/bootasm.S的start地址处
c //continue简称表示继续执行
x /2i $pc //显示当前eip处的汇编指令
set architecture i386 //设置当前调试的CPU是80386
运行"make debug"便可得到
Breakpoint 2, 0x00007c00 in ?? ()
=> 0x7c00: cli
0x7c01: cld
0x7c02: xor %eax,%eax
0x7c04: mov %eax,%ds
0x7c06: mov %eax,%es
0x7c08: mov %eax,%ss
0x7c0a: in $0x64,%al
0x7c0c: test $0x2,%al
0x7c0e: jne 0x7c0a
0x7c10: mov $0xd1,%al
[练习2.3] 在调用qemu 时增加-d in_asm -D q.log 参数便可以将运行的汇编指令保存在q.log 中。
将执行的汇编代码与bootasm.S 和 bootblock.asm 进行比较,看看二者是否一致。
在tools/gdbinit结尾加上
b *0x7c00
c
x /10i $pc
便可以在q.log中读到"call bootmain"前执行的命令
----------------
IN:
0x00007c00: cli
----------------
IN:
0x00007c01: cld
0x00007c02: xor %ax,%ax
0x00007c04: mov %ax,%ds
0x00007c06: mov %ax,%es
0x00007c08: mov %ax,%ss
----------------
IN:
0x00007c0a: in $0x64,%al
----------------
IN:
0x00007c0c: test $0x2,%al
0x00007c0e: jne 0x7c0a
----------------
IN:
0x00007c10: mov $0xd1,%al
0x00007c12: out %al,$0x64
0x00007c14: in $0x64,%al
0x00007c16: test $0x2,%al
0x00007c18: jne 0x7c14
----------------
IN:
0x00007c1a: mov $0xdf,%al
0x00007c1c: out %al,$0x60
0x00007c1e: lgdtw 0x7c6c
0x00007c23: mov %cr0,%eax
0x00007c26: or $0x1,%eax
0x00007c2a: mov %eax,%cr0
----------------
IN:
0x00007c2d: ljmp $0x8,$0x7c32
----------------
IN:
0x00007c32: mov $0x10,%ax
0x00007c36: mov %eax,%ds
----------------
IN:
0x00007c38: mov %eax,%es
----------------
IN:
0x00007c3a: mov %eax,%fs
0x00007c3c: mov %eax,%gs
0x00007c3e: mov %eax,%ss
----------------
IN:
0x00007c40: mov $0x0,%ebp
----------------
IN:
0x00007c45: mov $0x7c00,%esp
0x00007c4a: call 0x7d0d
----------------
IN:
0x00007d0d: push %ebp
其与bootasm.S和bootblock.asm中的代码相同。
[练习3] 分析bootloader 进入保护模式的过程。
从%cs=0 $pc=0x7c00进入后
首先清理环境包括将flag置0和将段寄存器置0
.code16
cli
cld
xorw %ax, %ax
movw %ax, %ds
movw %ax, %es
movw %ax, %ss
开启A20通过将键盘控制器上的A20线置于高电位全部32条地址线可用
可以访问4G的内存空间。
seta20.1: # 等待8042键盘控制器不忙
inb $0x64, %al #
testb $0x2, %al #
jnz seta20.1 #
movb $0xd1, %al # 发送写8042输出端口的指令
outb %al, $0x64 #
seta20.1: # 等待8042键盘控制器不忙
inb $0x64, %al #
testb $0x2, %al #
jnz seta20.1 #
movb $0xdf, %al # 打开A20
outb %al, $0x60 #
初始化GDT表一个简单的GDT表和其描述符已经静态储存在引导区中载入即可
lgdt gdtdesc
进入保护模式通过将cr0寄存器PE位置1便开启了保护模式
movl %cr0, %eax
orl $CR0_PE_ON, %eax
movl %eax, %cr0
通过长跳转更新cs的基地址
ljmp $PROT_MODE_CSEG, $protcseg
.code32
protcseg:
设置段寄存器,并建立堆栈
movw $PROT_MODE_DSEG, %ax
movw %ax, %ds
movw %ax, %es
movw %ax, %fs
movw %ax, %gs
movw %ax, %ss
movl $0x0, %ebp
movl $start, %esp
转到保护模式完成进入boot主方法
call bootmain
[练习4] 分析bootloader加载ELF格式的OS的过程。
首先看readsect函数
readsect从设备的第secno扇区读取数据到dst位置
static void
readsect(void *dst, uint32_t secno) {
waitdisk();
outb(0x1F2, 1); // 设置读取扇区的数目为1
outb(0x1F3, secno & 0xFF);
outb(0x1F4, (secno >> 8) & 0xFF);
outb(0x1F5, (secno >> 16) & 0xFF);
outb(0x1F6, ((secno >> 24) & 0xF) | 0xE0);
// 上面四条指令联合制定了扇区号
// 在这4个字节线联合构成的32位参数中
// 29-31位强制设为1
// 28位(=0)表示访问"Disk 0"
// 0-27位是28位的偏移量
outb(0x1F7, 0x20); // 0x20命令读取扇区
waitdisk();
insl(0x1F0, dst, SECTSIZE / 4); // 读取到dst位置
// 幻数4因为这里以DW为单位
}
readseg简单包装了readsect可以从设备读取任意长度的内容。
static void
readseg(uintptr_t va, uint32_t count, uint32_t offset) {
uintptr_t end_va = va + count;
va -= offset % SECTSIZE;
uint32_t secno = (offset / SECTSIZE) + 1;
// 加1因为0扇区被引导占用
// ELF文件从1扇区开始
for (; va < end_va; va += SECTSIZE, secno ++) {
readsect((void *)va, secno);
}
}
在bootmain函数中
void
bootmain(void) {
// 首先读取ELF的头部
readseg((uintptr_t)ELFHDR, SECTSIZE * 8, 0);
// 通过储存在头部的幻数判断是否是合法的ELF文件
if (ELFHDR->e_magic != ELF_MAGIC) {
goto bad;
}
struct proghdr *ph, *eph;
// ELF头部有描述ELF文件应加载到内存什么位置的描述表
// 先将描述表的头地址存在ph
ph = (struct proghdr *)((uintptr_t)ELFHDR + ELFHDR->e_phoff);
eph = ph + ELFHDR->e_phnum;
// 按照描述表将ELF文件中数据载入内存
for (; ph < eph; ph ++) {
readseg(ph->p_va & 0xFFFFFF, ph->p_memsz, ph->p_offset);
}
// ELF文件0x1000位置后面的0xd1ec比特被载入内存0x00100000
// ELF文件0xf000位置后面的0x1d20比特被载入内存0x0010e000
// 根据ELF头部储存的入口信息找到内核的入口
((void (*)(void))(ELFHDR->e_entry & 0xFFFFFF))();
bad:
outw(0x8A00, 0x8A00);
outw(0x8A00, 0x8E00);
while (1);
}
[练习5] 实现函数调用堆栈跟踪函数
ss:ebp指向的堆栈位置储存着caller的ebp以此为线索可以得到所有使用堆栈的函数ebp。
ss:ebp+4指向caller调用时的eipss:ebp+8等是可能的参数。
输出中,堆栈最深一层为
ebp:0x00007bf8 eip:0x00007d68 \
args:0x00000000 0x00000000 0x00000000 0x00007c4f
<unknow>: -- 0x00007d67 --
其对应的是第一个使用堆栈的函数bootmain.c中的bootmain。
bootloader设置的堆栈从0x7c00开始使用"call bootmain"转入bootmain函数。
call指令压栈所以bootmain中ebp为0x7bf8。
[练习6] 完善中断初始化和处理
[练习6.1] 中断向量表中一个表项占多少字节?其中哪几位代表中断处理代码的入口?
中断向量表一个表项占用8字节其中2-3字节是段选择子0-1字节和6-7字节拼成位移
两者联合便是中断处理程序的入口地址。
[练习6.2] 请编程完善kern/trap/trap.c中对中断向量表进行初始化的函数idt_init。
见代码
[练习6.3] 请编程完善trap.c中的中断处理函数trap在对时钟中断进行处理的部分填写trap函数
见代码
[练习7] 增加syscall功能即增加一用户态函数可执行一特定系统调用获得时钟计数值
当内核初始完毕后,可从内核态返回到用户态的函数,而用户态的函数又通过系统调用得到内核态的服务
在idt_init中将用户态调用SWITCH_TOK中断的权限打开。
SETGATE(idt[T_SWITCH_TOK], 1, KERNEL_CS, __vectors[T_SWITCH_TOK], 3);
在trap_dispatch中将iret时会从堆栈弹出的段寄存器进行修改
对TO User
tf->tf_cs = USER_CS;
tf->tf_ds = USER_DS;
tf->tf_es = USER_DS;
tf->tf_ss = USER_DS;
对TO Kernel
tf->tf_cs = KERNEL_CS;
tf->tf_ds = KERNEL_DS;
tf->tf_es = KERNEL_DS;
在lab1_switch_to_user中调用T_SWITCH_TOU中断。
注意从中断返回时会多pop两位并用这两位的值更新ss,sp损坏堆栈。
所以要先把栈压两位并在从中断返回后修复esp。
asm volatile (
"sub $0x8, %%esp \n"
"int %0 \n"
"movl %%ebp, %%esp"
:
: "i"(T_SWITCH_TOU)
);
在lab1_switch_to_kernel中调用T_SWITCH_TOK中断。
注意从中断返回时esp仍在TSS指示的堆栈中。所以要在从中断返回后修复esp。
asm volatile (
"int %0 \n"
"movl %%ebp, %%esp \n"
:
: "i"(T_SWITCH_TOK)
);
但这样不能正常输出文本。根据提示在trap_dispatch中转User态时将调用io所需权限降低。
tf->tf_eflags |= 0x3000;

View File

@ -70,7 +70,7 @@ endef
# finish all # finish all
define do_finish_all define do_finish_all
ALLDEPS = $$(ALLOBJS:.o=.d) ALLDEPS = $$(ALLOBJS:.o=.d)
$$(sort $$(dir $$(ALLOBJS)) $(BINDIR) $(OBJDIR)): $$(sort $$(dir $$(ALLOBJS)) $(BINDIR)$(SLASH) $(OBJDIR)$(SLASH)):
@$(MKDIR) $$@ @$(MKDIR) $$@
endef endef

View File

@ -0,0 +1,6 @@
file bin/kernel
target remote :1234
set architecture i8086
b *0x7c00
continue
x /2i $pc

View File

@ -0,0 +1,3 @@
file bin/kernel
target remote :1234
break kern_init

View File

@ -23,8 +23,8 @@ endif
# try to infer the correct QEMU # try to infer the correct QEMU
ifndef QEMU ifndef QEMU
QEMU := $(shell if which qemu > /dev/null; \ QEMU := $(shell if which qemu-system-i386 > /dev/null; \
then echo 'qemu'; exit; \ then echo 'qemu-system-i386'; exit; \
elif which i386-ucore-elf-qemu > /dev/null; \ elif which i386-ucore-elf-qemu > /dev/null; \
then echo 'i386-ucore-elf-qemu'; exit; \ then echo 'i386-ucore-elf-qemu'; exit; \
else \ else \
@ -208,6 +208,8 @@ targets: $(TARGETS)
QEMUOPTS = -hda $(UCOREIMG) QEMUOPTS = -hda $(UCOREIMG)
.PHONY: qemu qemu-nox gdb debug debug-mon debug-nox .PHONY: qemu qemu-nox gdb debug debug-mon debug-nox
qemu-mon: targets
$(V)$(QEMU) -monitor stdio $(QEMUOPTS) -serial null
qemu: targets qemu: targets
$(V)$(QEMU) -parallel stdio $(QEMUOPTS) -serial null $(V)$(QEMU) -parallel stdio $(QEMUOPTS) -serial null

View File

@ -23,8 +23,8 @@ endif
# try to infer the correct QEMU # try to infer the correct QEMU
ifndef QEMU ifndef QEMU
QEMU := $(shell if which qemu > /dev/null; \ QEMU := $(shell if which qemu-system-i386 > /dev/null; \
then echo 'qemu'; exit; \ then echo 'qemu-system-i386'; exit; \
elif which i386-ucore-elf-qemu > /dev/null; \ elif which i386-ucore-elf-qemu > /dev/null; \
then echo 'i386-ucore-elf-qemu'; exit; \ then echo 'i386-ucore-elf-qemu'; exit; \
else \ else \
@ -216,6 +216,8 @@ TARGETS: $(TARGETS)
QEMUOPTS = -hda $(UCOREIMG) -drive file=$(SWAPIMG),media=disk,cache=writeback QEMUOPTS = -hda $(UCOREIMG) -drive file=$(SWAPIMG),media=disk,cache=writeback
.PHONY: qemu qemu-nox debug debug-nox .PHONY: qemu qemu-nox debug debug-nox
qemu-mon: $(UCOREIMG) $(SWAPIMG)
$(V)$(QEMU) -monitor stdio $(QEMUOPTS) -serial null
qemu: $(UCOREIMG) $(SWAPIMG) qemu: $(UCOREIMG) $(SWAPIMG)
$(V)$(QEMU) -parallel stdio $(QEMUOPTS) -serial null $(V)$(QEMU) -parallel stdio $(QEMUOPTS) -serial null

View File

@ -23,8 +23,8 @@ endif
# try to infer the correct QEMU # try to infer the correct QEMU
ifndef QEMU ifndef QEMU
QEMU := $(shell if which qemu > /dev/null; \ QEMU := $(shell if which qemu-system-i386 > /dev/null; \
then echo 'qemu'; exit; \ then echo 'qemu-system-i386'; exit; \
elif which i386-ucore-elf-qemu > /dev/null; \ elif which i386-ucore-elf-qemu > /dev/null; \
then echo 'i386-ucore-elf-qemu'; exit; \ then echo 'i386-ucore-elf-qemu'; exit; \
else \ else \
@ -220,6 +220,8 @@ TARGETS: $(TARGETS)
QEMUOPTS = -hda $(UCOREIMG) -drive file=$(SWAPIMG),media=disk,cache=writeback QEMUOPTS = -hda $(UCOREIMG) -drive file=$(SWAPIMG),media=disk,cache=writeback
.PHONY: qemu qemu-nox debug debug-nox .PHONY: qemu qemu-nox debug debug-nox
qemu-mon: $(UCOREIMG) $(SWAPIMG)
$(V)$(QEMU) -monitor stdio $(QEMUOPTS) -serial null
qemu: $(UCOREIMG) $(SWAPIMG) qemu: $(UCOREIMG) $(SWAPIMG)
$(V)$(QEMU) -parallel stdio $(QEMUOPTS) -serial null $(V)$(QEMU) -parallel stdio $(QEMUOPTS) -serial null

View File

@ -23,8 +23,8 @@ endif
# try to infer the correct QEMU # try to infer the correct QEMU
ifndef QEMU ifndef QEMU
QEMU := $(shell if which qemu > /dev/null; \ QEMU := $(shell if which qemu-system-i386 > /dev/null; \
then echo 'qemu'; exit; \ then echo 'qemu-system-i386'; exit; \
elif which i386-ucore-elf-qemu > /dev/null; \ elif which i386-ucore-elf-qemu > /dev/null; \
then echo 'i386-ucore-elf-qemu'; exit; \ then echo 'i386-ucore-elf-qemu'; exit; \
else \ else \
@ -259,6 +259,8 @@ TARGETS: $(TARGETS)
QEMUOPTS = -hda $(UCOREIMG) -drive file=$(SWAPIMG),media=disk,cache=writeback QEMUOPTS = -hda $(UCOREIMG) -drive file=$(SWAPIMG),media=disk,cache=writeback
.PHONY: qemu qemu-nox debug debug-nox .PHONY: qemu qemu-nox debug debug-nox
qemu-mon: $(UCOREIMG) $(SWAPIMG)
$(V)$(QEMU) -monitor stdio $(QEMUOPTS) -serial null
qemu: $(UCOREIMG) $(SWAPIMG) qemu: $(UCOREIMG) $(SWAPIMG)
$(V)$(QEMU) -parallel stdio $(QEMUOPTS) -serial null $(V)$(QEMU) -parallel stdio $(QEMUOPTS) -serial null

View File

@ -23,8 +23,8 @@ endif
# try to infer the correct QEMU # try to infer the correct QEMU
ifndef QEMU ifndef QEMU
QEMU := $(shell if which qemu > /dev/null; \ QEMU := $(shell if which qemu-system-i386 > /dev/null; \
then echo 'qemu'; exit; \ then echo 'qemu-system-i386'; exit; \
elif which i386-ucore-elf-qemu > /dev/null; \ elif which i386-ucore-elf-qemu > /dev/null; \
then echo 'i386-ucore-elf-qemu'; exit; \ then echo 'i386-ucore-elf-qemu'; exit; \
else \ else \
@ -259,6 +259,8 @@ TARGETS: $(TARGETS)
QEMUOPTS = -hda $(UCOREIMG) -drive file=$(SWAPIMG),media=disk,cache=writeback QEMUOPTS = -hda $(UCOREIMG) -drive file=$(SWAPIMG),media=disk,cache=writeback
.PHONY: qemu qemu-nox debug debug-nox .PHONY: qemu qemu-nox debug debug-nox
qemu-mon: $(UCOREIMG) $(SWAPIMG)
$(V)$(QEMU) -monitor stdio $(QEMUOPTS) -serial null
qemu: $(UCOREIMG) $(SWAPIMG) qemu: $(UCOREIMG) $(SWAPIMG)
$(V)$(QEMU) -parallel stdio $(QEMUOPTS) -serial null $(V)$(QEMU) -parallel stdio $(QEMUOPTS) -serial null

View File

@ -23,8 +23,8 @@ endif
# try to infer the correct QEMU # try to infer the correct QEMU
ifndef QEMU ifndef QEMU
QEMU := $(shell if which qemu > /dev/null; \ QEMU := $(shell if which qemu-system-i386 > /dev/null; \
then echo 'qemu'; exit; \ then echo 'qemu-system-i386'; exit; \
elif which i386-ucore-elf-qemu > /dev/null; \ elif which i386-ucore-elf-qemu > /dev/null; \
then echo 'i386-ucore-elf-qemu'; exit; \ then echo 'i386-ucore-elf-qemu'; exit; \
else \ else \
@ -259,6 +259,8 @@ TARGETS: $(TARGETS)
QEMUOPTS = -hda $(UCOREIMG) -drive file=$(SWAPIMG),media=disk,cache=writeback QEMUOPTS = -hda $(UCOREIMG) -drive file=$(SWAPIMG),media=disk,cache=writeback
.PHONY: qemu qemu-nox debug debug-nox .PHONY: qemu qemu-nox debug debug-nox
qemu-mon: $(UCOREIMG) $(SWAPIMG)
$(V)$(QEMU) -monitor stdio $(QEMUOPTS) -serial null
qemu: $(UCOREIMG) $(SWAPIMG) qemu: $(UCOREIMG) $(SWAPIMG)
$(V)$(QEMU) -parallel stdio $(QEMUOPTS) -serial null $(V)$(QEMU) -parallel stdio $(QEMUOPTS) -serial null

View File

@ -23,8 +23,8 @@ endif
# try to infer the correct QEMU # try to infer the correct QEMU
ifndef QEMU ifndef QEMU
QEMU := $(shell if which qemu > /dev/null; \ QEMU := $(shell if which qemu-system-i386 > /dev/null; \
then echo 'qemu'; exit; \ then echo 'qemu-system-i386'; exit; \
elif which i386-ucore-elf-qemu > /dev/null; \ elif which i386-ucore-elf-qemu > /dev/null; \
then echo 'i386-ucore-elf-qemu'; exit; \ then echo 'i386-ucore-elf-qemu'; exit; \
else \ else \
@ -296,6 +296,8 @@ TARGETS: $(TARGETS)
QEMUOPTS = -hda $(UCOREIMG) -drive file=$(SWAPIMG),media=disk,cache=writeback -drive file=$(SFSIMG),media=disk,cache=writeback QEMUOPTS = -hda $(UCOREIMG) -drive file=$(SWAPIMG),media=disk,cache=writeback -drive file=$(SFSIMG),media=disk,cache=writeback
.PHONY: qemu qemu-nox debug debug-nox monitor .PHONY: qemu qemu-nox debug debug-nox monitor
qemu-mon: $(UCOREIMG) $(SWAPIMG) $(SFSIMG)
$(V)$(QEMU) -monitor stdio $(QEMUOPTS) -serial null
qemu: $(UCOREIMG) $(SWAPIMG) $(SFSIMG) qemu: $(UCOREIMG) $(SWAPIMG) $(SFSIMG)
$(V)$(QEMU) -parallel stdio $(QEMUOPTS) -serial null $(V)$(QEMU) -parallel stdio $(QEMUOPTS) -serial null

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.