清华大学操作系统实验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) # 数据段(起始地址,大小)