清华大学操作系统实验lab1实验报告
- 格式:docx
- 大小:497.89 KB
- 文档页数:14
练习1、理解通过make生成执行文件的过程。
[练习1.1] 操作系统镜像文件 ucore.img 是如何一步一步生成的?
在proj1执行命令make V=可以得到make指令执行的过程
从这几条指令中可以看出需要生成ucore.img首先需要生成bootblock,而生成bootblock需要先生成bootmain.o和bootasm.o还有sign,这三个文件又分别由bootmain.c、bootasm.S、sigh.c来生成。
ld -m elf_i386 -N -e start -Ttext 0x7C00 obj/boot/bootasm.o
obj/boot/bootmain.o –
o obj/bootblock.o
这句话用于生成bootblock,elf_i386表示生成elf头,0x7C00为程序的入make V=
+ cc boot/bootasm.S
gcc -Iboot/ -fno-builtin -Wall -ggdb -m32 -nostdinc -fno-stack-protector -Ilibs/ -Os -nostdinc -c boot/bootasm.S -o obj/boot/bootasm.o
+ cc boot/bootmain.c
gcc -Iboot/ -fno-builtin -Wall -ggdb -m32 -nostdinc -fno-stack-protector -Ilibs/ -Os -nostdinc -c boot/bootmain.c -o obj/boot/bootmain.o
+ cc tools/sign.c
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
+ ld bin/bootblock
ld -m elf_i386 -N -e start -Ttext 0x7C00 obj/boot/bootasm.o
obj/boot/bootmain.o -o obj/bootblock.o
'obj/bootblock.out' size: 440 bytes
build 512 bytes boot sector: 'bin/bootblock' success!
dd if=/dev/zero of=bin/ucore.img count=10000
记录了10000+0 的读入
记录了10000+0 的写出
5120000字节(5.1 MB)已复制,0.0227439 秒,225 MB/秒
dd if=bin/bootblock of=bin/ucore.img conv=notrunc
记录了1+0 的读入
记录了1+0 的写出
512字节(512 B)已复制,0.000214966 秒,2.4 MB/秒
口。
'obj/bootblock.out' size: 440 bytes
这句话表示生成的bootblock的文件大小,因为大小不到512字节,所以需要给blootblock填充,填充的功能在sign.c中有所体现,最后两字节设置为了0x55,0xAA
buf[510] = 0x55;
buf[511] = 0xAA;
FILE *ofp = fopen(argv[2], "wb+");
size = fwrite(buf, 1, 512, ofp);
[练习1.2] 一个被系统认为是符合规范的硬盘主引导扇区的特征是什么?
前面已经提到过:引导扇区的大小为512字节,最后两个字节为标志性结束字节0x55,0xAA,做完这样的检查才能认为是符合规范的磁盘主引导扇区。
Sign.c文件中有作检查:
if (size != 512) {
fprintf(stderr, "write '%s' error, size is %d.\n", argv[2], size);
return -1;
}
练习2:使用qemu执行并调试lab1中的软件。
[练习2.1] 从 CPU 加电后执行的第一条指令开始,单步跟踪 BIOS 的执行。
[练习2.2] 在初始化位置0x7c00 设置实地址断点,测试断点正常。
[练习2.3] 在调用qemu 时增加-d in_asm -D q.log 参数,便可以将运行的汇编指令保存在q.log 中。将执行的汇编代码与bootasm.S 和 bootblock.asm
进行比较,看看二者是否一致。 实验是基于老lab1/proj1做的,练习开始时是打算用命令行一句一句执行得到结果的,后来发现直接修改makefile和gdbinit可以大大提高调试效率。
于是在makefile中增加以下代码
-S –s是使得qemu在执行第一条指令之前停下来,在调用qemu时增加-d in_asm -D q.log参数,便可以将运行的汇编指令保存在q.log中。然后sleep两秒应该是给qemu充分的时间准备等待连接。接下来使用GDB调试工具,-tui提供了代码与命令行分屏查看的界面,tools/gdbinit中存放的是gdb调试指令如下。
先是加载调试文件,然后连接qemu,设置8086的实模式,设置断点0x7c00,也就是bootloader的第一条指令,然后运行到断点。再显示接下来的10条指令。
运行结果图如下 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 -tui -x tools/gdbinit"
file obj/bootblock.o
target remote :1234
set architecture i8086
b *0x7c00
continue
x /10i $pc
很明显,断点位置的代码和boot.asm文件中的代码完全一致,说明断点设置成功。打开q.log文件看,看到了很奇葩的结果。能够看到cli,cld之类熟悉的指令,但是他们的地址以及出现的顺序都不是想象的那样(从0x0x00007c00k开始,第一条指令为cld)。
之后听大神解释,在q.log中进入BIOS之后的跳转地址与实际应跳转地址不相符,汇编代码也 与bootasm.S 和 bootblock.asm不相同。 可以通过make debug之后在qemu的控制台中输入x /10i $pc看到BIOS执行bootloader部分的代码。
进过对比,这些代码 与bootasm.S与bootblock.asm中的代码完全一致。
练习3 分析bootloader 进入保护模式的过程。
在开启A20之前,BIOS还做了很多事:关中断、清除方向标志,给各个数据段清零。 cli # Disable interrupts
cld # String operations
increment
# Set up the important data segment registers (DS, ES, SS).
xorw %ax, %ax # Segment number zero
movw %ax, %ds # -> Data Segment
movw %ax, %es # -> Extra Segment
movw %ax, %ss 1、为何开启A20,以及如何开启A20?
当 A20 地址线控制禁止时,则程序就像在 8086 中运行,1MB 以上的地是不可访问的。 在保护模式下 A20 地址线控制是要打开的。为了使能所有地址位的寻址能力,必须向键盘控 制器 8042 发送一个命令。键盘控制器
8042 将会将它的的某个输出引脚的输出置高电平,作 为 A20 地址线控制的输入。一旦设置成功之后,内存将不会再被绕回(memory wrapping),这 样我们就可以寻址 intel 80286 CPU 支持的 16M 内存空间,或者是寻址 intel
80386 以上级别 CPU 支持的所有 4G 内存空间了。
2、如何初始化GDT表? seta20.1:
inb $0x64, %al # 等待8042键盘控制器不忙
testb $0x2, %al
jnz seta20.1
movb $0xd1, %al # 发送写8042输出端口的指令
outb %al, $0x64
seta20.2:
inb $0x64, %al # 等待8042键盘控制器不忙
testb $0x2, %al
jnz seta20.2
movb $0xdf, %al # 打开A20
outb %al, $0x60
lgdt gdtdesc #把gdt表的起始位置和界限装入GDTR寄存器
movl %cr0, %eax
orl $CR0_PE_ON, %eax
movl %eax, %cr0 #把保护模式位开启
复习一下cr0寄存器,它的第0位为保护模式位PE:设置 PE 将让处理器工作在保护模式下。复位PE将返回到实模式工作。此外,gdtdesc指出了全局描述符表在符号gdt处,如下
上面四句话实现了打开保护模式位。
3、如何使能进入保护模式?
通过长跳转指令
ljmp $PROT_MODE_CSEG, $protcseg
进入了保护模式。
进入保护模式之后还有一个步骤:把所有的数据段寄存器指向上面的GDT描述符表中的数据段(0x10)。
练习四、分析bootloader加载ELF格式的OS的过程。 gdt:
SEG_NULLASM # 空段
SEG_ASM(STA_X|STA_R, 0x0, 0xffffffff) # 代码段(起始地址,大小)
SEG_ASM(STA_W, 0x0, 0xffffffff) # 数据段(起始地址,大小)