ucore操作系统实验学习笔记2

1.实验一

实验一主要是关注操bootloader的作用,同时练习QEMU和gdb的联合调试。

1.1练习1

  1. 操作系统镜像ucore.img是如何一步一步生成的?
    通过分析 Makefile的指令可以看出,ucore.img 里面包含了两部分内容:第一部分是 bootloader, 这部分的大小是512bytes,这部分内容就是 bin/bootblock 文件; 主要由三个源文件文件编译链接而成:asm.h, bootasm.S, bootmain.c。第二部分是 kernel, 这部分大小是74868 bytes.这部分就是ucore的内容,主要由源文件 kern/ 下面的文件编译连接而成(分别将每个子目录下的文件生成.o文件,然后将所有.o文件使用 ld 命令链接起来形成kernel文件), 生成的二进制文件是 /bin/kernel。 ucore.img 的大小是 512 X 10000 bytes. 初始时里面全部填充为 0. 使用 dd 命令分别将 bootblock 和 kernel 这个两个文件复制进入 ucore.img 中。

  2. 一个被系统认为是符合规范的硬盘主引导扇区的特征是什么?
    首先一个主引导扇区的大小只能是512 bytes. 其次,这512字节的最后两个bytes 分别是 0x55, 0xaa。

1.2练习2

  1. 从CPU加电后执行的第一条指令开始,单步跟踪BIOS的执行。
    BIOS是固化在系统ROM中的一段固件,早期的系统使用是的BIOS,现代计算机早已经用UEFI取代了BIOS。BIOS完成了系统硬件初始化和加载主引导扇区的功能,BIOS(UEFI)是由厂商的提供的,在系统一上电就开始执行,因此我们操作系统课程里面的编写的代码和BIOS没有关系。查阅资料可知,QEMU使用的是一个叫 seabios 的项目中的代码,可以在 github 中查看seabios 的源码: https://github.com/qemu/seabios , 同时 seabios 的项目主页上也提供调试seabios的方法: https://www.seabios.org/Debugging

想要单步执行 seabios 的话,可以先从在一个命令行中输入:

$qemu-system-i386 -fda ./myimage.img -S -s

此时可以观察到qemu 启动了一个窗口,但是处于等待状态,没有任何输出。可以在另外一个终端输入:
$gdb

$set architecture i8086

$target remote localhost:1234

此时便可以在gdb中打印和查看BIOS的指令了。

1.3练习3

  1. 为何开启A20,以及如何开启A20
    由于在保护模式下需要访问4GB的内存空间,所以需要将A20地址线enable。
    在bootasm.S 代码中,使能A20的代码段如下:
    seta20.1
    inb $0x64, %al #从port 64读取状态寄存器信息到 al
    testb $0x2, %al #测试input bufffer 是否有数据
    jnz seta20.1
    movb $0xd1, %al
    outb %al, $0x64
    seta20.2
    inb $0x64,%al
    testb $0x2, %al
    jnz seta20.2
    movb $0xdf, %al
    outb %al, $0x60

  2. 如何初始化GDT表
    建立GDT的函数如下(在 asm.h 中):

#define SEG_ASM(type,base,lim) \
.word (((lim) >> 12) & 0xffff), ((base) & 0xffff); \
.byte (((base) >> 16) & 0xff), (0x90 | (type)), \
(0xC0 | (((lim) >> 28) & 0xf)), (((base) >> 24) & 0xff)

在bootasm.S中调用上述函数建立GDT:

gdt:
SEG_NULLASM # null seg
SEG_ASM(STA_X|STA_R, 0x0, 0xffffffff) # code seg for bootloader and kernel
SEG_ASM(STA_W, 0x0, 0xffffffff) # data seg for bootloader and kernel

  1. 如何使能和进入保护模式
    进入保护模式即为将CR0寄存器的 bit0 设置为 1.

1.4练习4

  1. bootloader如何读取硬盘扇区的?
    对硬盘扇区的读取主要由 bootmain.c 文件中的2个函数实现: readsect 和 readseg
    readsect 每次读一个扇区
    readseg 则指定了要读取的字节数

  2. bootloader是如何加载ELF格式的OS?
    bootloader通过验证ELFHDR->e_magic来确认读取的是否为elf格式文件。

1.5练习5

1.6练习6

  1. 中断描述符表(也可简称为保护模式下的中断向量表)中一个表项占多少字节?其中哪几位代表中断处理代码的入口?
    中断描述符表(Interrupt Descriptor Table, IDT)中的每个表项占据 8-byte (according to Intel i386 programmer manual).
    根据中断描述符的定义:最低16位 和 最高的16位组成的 offset 是指向中断处理函数的地址。

  2. 请编程完善kern/trap/trap.c中对中断向量表进行初始化的函数idt_init。在idt_init函数中,依次对所有中断入口进行初始化。使用mmu.h中的SETGATE宏,填充idt数组内容。每个中断的入口由tools/vectors.c生成,使用trap.c中声明的vectors数组即可。

extern uintptr_t __vectors[];
int i;
for (i = 0; i < 256; i++) {
SETGATE(idt[i], 0, GD_KTEXT, __vectors[i], 0);

SETGATE(idt[T_SWITCH_TOK], 0, GD_KTEXT, __vectors[T_SWITCH_TOK], 0);

lidt(&idt_pd);

  1. 请编程完善trap.c中的中断处理函数trap,在对时钟中断进行处理的部分填写trap函数中处理时钟中断的部分,使操作系统每遇到100次时钟中断后,调用print_ticks子程序,向屏幕上打印一行文字”100 ticks”。

只需要在函数 trap_dispatch() 中添加以下语句即可:
ticks++;
if (ticks % TICK_NUM == 0)
print_ticks();

猜你喜欢

转载自www.cnblogs.com/wangxiaoyong/p/12568349.html
今日推荐