labworkP01

实验报告

实验内容

  1. 阅读 uCore 实验项目开始文档 (uCore Lab 0),准备实验平台,熟悉实验工具。

  2. uCore Lab 1:系统软件启动过程

(1) 编译运行 uCore Lab 1 的工程代码;

(2) 完成 uCore Lab 1 练习 1-4 的实验报告;

(3) 尝试实现 uCore Lab 1 练习 5-6 的编程作业;

(4) 思考如何实现 uCore Lab 1 扩展练习 1-2。

实验环境

 Ubuntu 14.04(64位)

实验过程

练习1:理解通过make生成执行文件的过程。

  1. 操作系统镜像文件ucore.img是如何一步一步生成的?(需要比较详细地解释Makefile中每一条相关命令和命令参数的含义,以及说明命令导致的结果)

生成 kernel:
在这里插入图片描述
生成 bootblock:
在这里插入图片描述
生成 ucore.img:
在这里插入图片描述

// 根据 Makefile 编译链接整个工程,并将详情显示出来
moocos-> make "V="

// 编译 init.c
+ cc kern/init/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
kern/init/init.c:95:1: warning: ‘lab1_switch_test’ defined but not used [-Wunused-function]
 lab1_switch_test(void) {
 ^

// 编译 readline.c
+ cc kern/libs/readline.c
gcc -Ikern/libs/ -fno-builtin -Wall -ggdb -m32 -gstabs -nostdinc  -fno-stack-protector -Ilibs/ -Ikern/debug/ -Ikern/driver/ -Ikern/trap/ -Ikern/mm/ -c kern/libs/readline.c -o obj/kern/libs/readline.o

// 编译 stdio.c
+ cc kern/libs/stdio.c
gcc -Ikern/libs/ -fno-builtin -Wall -ggdb -m32 -gstabs -nostdinc  -fno-stack-protector -Ilibs/ -Ikern/debug/ -Ikern/driver/ -Ikern/trap/ -Ikern/mm/ -c kern/libs/stdio.c -o obj/kern/libs/stdio.o

// 编译 kdebug.c
+ cc kern/debug/kdebug.c
gcc -Ikern/debug/ -fno-builtin -Wall -ggdb -m32 -gstabs -nostdinc  -fno-stack-protector -Ilibs/ -Ikern/debug/ -Ikern/driver/ -Ikern/trap/ -Ikern/mm/ -c kern/debug/kdebug.c -o obj/kern/debug/kdebug.o
kern/debug/kdebug.c:251:1: warning: ‘read_eip’ defined but not used [-Wunused-function]
 read_eip(void) {
 ^

// 编译 kmonitor.c
+ cc kern/debug/kmonitor.c
gcc -Ikern/debug/ -fno-builtin -Wall -ggdb -m32 -gstabs -nostdinc  -fno-stack-protector -Ilibs/ -Ikern/debug/ -Ikern/driver/ -Ikern/trap/ -Ikern/mm/ -c kern/debug/kmonitor.c -o obj/kern/debug/kmonitor.o

// 编译 panic.c
+ cc kern/debug/panic.c
gcc -Ikern/debug/ -fno-builtin -Wall -ggdb -m32 -gstabs -nostdinc  -fno-stack-protector -Ilibs/ -Ikern/debug/ -Ikern/driver/ -Ikern/trap/ -Ikern/mm/ -c kern/debug/panic.c -o obj/kern/debug/panic.o

// 编译 clock.c
+ cc kern/driver/clock.c
gcc -Ikern/driver/ -fno-builtin -Wall -ggdb -m32 -gstabs -nostdinc  -fno-stack-protector -Ilibs/ -Ikern/debug/ -Ikern/driver/ -Ikern/trap/ -Ikern/mm/ -c kern/driver/clock.c -o obj/kern/driver/clock.o

// 编译 console.c
+ cc kern/driver/console.c
gcc -Ikern/driver/ -fno-builtin -Wall -ggdb -m32 -gstabs -nostdinc  -fno-stack-protector -Ilibs/ -Ikern/debug/ -Ikern/driver/ -Ikern/trap/ -Ikern/mm/ -c kern/driver/console.c -o obj/kern/driver/console.o

// 编译 intr.c
+ cc kern/driver/intr.c
gcc -Ikern/driver/ -fno-builtin -Wall -ggdb -m32 -gstabs -nostdinc  -fno-stack-protector -Ilibs/ -Ikern/debug/ -Ikern/driver/ -Ikern/trap/ -Ikern/mm/ -c kern/driver/intr.c -o obj/kern/driver/intr.o

// 编译 picirq.c
+ cc kern/driver/picirq.c
gcc -Ikern/driver/ -fno-builtin -Wall -ggdb -m32 -gstabs -nostdinc  -fno-stack-protector -Ilibs/ -Ikern/debug/ -Ikern/driver/ -Ikern/trap/ -Ikern/mm/ -c kern/driver/picirq.c -o obj/kern/driver/picirq.o

// 编译 trap.c
+ cc kern/trap/trap.c
gcc -Ikern/trap/ -fno-builtin -Wall -ggdb -m32 -gstabs -nostdinc  -fno-stack-protector -Ilibs/ -Ikern/debug/ -Ikern/driver/ -Ikern/trap/ -Ikern/mm/ -c kern/trap/trap.c -o obj/kern/trap/trap.o
kern/trap/trap.c:14:13: warning: ‘print_ticks’ defined but not used [-Wunused-function]
 static void print_ticks() {
             ^
kern/trap/trap.c:30:26: warning: ‘idt_pd’ defined but not used [-Wunused-variable]
 static struct pseudodesc idt_pd = {
                          ^

// 编译 trapentry.S
+ cc kern/trap/trapentry.S
gcc -Ikern/trap/ -fno-builtin -Wall -ggdb -m32 -gstabs -nostdinc  -fno-stack-protector -Ilibs/ -Ikern/debug/ -Ikern/driver/ -Ikern/trap/ -Ikern/mm/ -c kern/trap/trapentry.S -o obj/kern/trap/trapentry.o

// 编译 vectors.S
+ cc kern/trap/vectors.S
gcc -Ikern/trap/ -fno-builtin -Wall -ggdb -m32 -gstabs -nostdinc  -fno-stack-protector -Ilibs/ -Ikern/debug/ -Ikern/driver/ -Ikern/trap/ -Ikern/mm/ -c kern/trap/vectors.S -o obj/kern/trap/vectors.o

// 编译 pmm.c
+ cc kern/mm/pmm.c
gcc -Ikern/mm/ -fno-builtin -Wall -ggdb -m32 -gstabs -nostdinc  -fno-stack-protector -Ilibs/ -Ikern/debug/ -Ikern/driver/ -Ikern/trap/ -Ikern/mm/ -c kern/mm/pmm.c -o obj/kern/mm/pmm.o

// 编译 printfmt.c
+ cc libs/printfmt.c
gcc -Ilibs/ -fno-builtin -Wall -ggdb -m32 -gstabs -nostdinc  -fno-stack-protector -Ilibs/  -c libs/printfmt.c -o obj/libs/printfmt.o

// 编译 string.c
+ cc libs/string.c
gcc -Ilibs/ -fno-builtin -Wall -ggdb -m32 -gstabs -nostdinc  -fno-stack-protector -Ilibs/  -c libs/string.c -o obj/libs/string.o

// 将所依赖的已编译文件链接成 kernel
+ ld bin/kernel
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

// 编译 bootasm.S
+ cc boot/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

// 编译 bootmain.c
+ cc boot/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

// 编译 sign.c
+ 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

// 将所依赖的已编译文件链接成 bootblock
+ ld bin/bootblock
ld -m    elf_i386 -nostdlib -N -e start -Ttext 0x7C00 obj/boot/bootasm.o obj/boot/bootmain.o -o obj/bootblock.o

'obj/bootblock.out' size: 472 bytes
build 512 bytes boot sector: 'bin/bootblock' success!

// 生成 ucore.img,大小为10000个块,初始化为0,每个块为512字节
dd if=/dev/zero of=bin/ucore.img count=10000
10000+0 records in
10000+0 records out
5120000 bytes (5.1 MB) copied, 0.0491865 s, 104 MB/s

// ucore.img 的第一个块写 bootblock
dd if=bin/bootblock of=bin/ucore.img conv=notrunc
1+0 records in
1+0 records out
512 bytes (512 B) copied, 0.000126092 s, 4.1 MB/s

// ucore.img 从第二个块开始写 kernel 中的内容
dd if=bin/kernel of=bin/ucore.img seek=1 conv=notrunc
138+1 records in
138+1 records out
70775 bytes (71 kB) copied, 0.000401104 s, 176 MB/s
[~/moocos/ucore_lab/labcodes/lab1]

ucore.img 的生成:

① 编译 kernel 所依赖的所有文件
② 链接生成 kernel
③ 编译 bootblock 所依赖的所有文件
④ 链接生成 bootblock
⑤ 根据 bootblock 和 kernel 生成 ucore.img
  1. 一个被系统认为是符合规范的硬盘主引导扇区的特征是什么?

    大小为512字节;空余空间填0;最后两位固定为0x55、0xAA。

练习2:使用qemu执行并调试lab1中的软件。

  1. 从CPU加电后执行的第一条指令开始,单步跟踪BIOS的执行。

(1) 修改 lab1/tools/gdbinit

set architecture i8086
target remote :1234

在这里插入图片描述
(2) 在 lab1目录下,执行

make debug

在这里插入图片描述
可以看到这时gdb停在BIOS的第一条指令处:

0xffff0: 

(3) 在看到gdb的调试界面(gdb)后,执行如下命令,就可以看到BIOS在执行了

si

在这里插入图片描述
(4) 查看BIOS的代码

x /2i 0xffff0

在这里插入图片描述
进一步可以执行,就可以看到后续的BIOS代码

x /10i 0xfe05b

(由于显示原因,无法将代码看全)
在这里插入图片描述
2. 在初始化位置0x7c00设置实地址断点,测试断点正常。

(1) 修改 lab1/tools/gdbinit
在这里插入图片描述

(2) 在 lab1目录下,执行

make debug

在这里插入图片描述
在这里插入图片描述
断点正常。

  1. 从0x7c00开始跟踪代码运行,将单步跟踪反汇编得到的代码与bootasm.S和 bootblock.asm进行比较。

单步跟踪:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

bootasm.S 和 bootblock.asm 的代码是一样的
在这里插入图片描述
对比之后,发现跟踪反汇编得到的代码与 bootasm.S 和 bootblock.asm 的代码是一样的。

  1. 自己找一个bootloader或内核中的代码位置,设置断点并进行测试。

在 0x7c32(protcseg:)处设置断点
在这里插入图片描述

make debug

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
测试结果与 bootasm.S 的代码一致,所以断点正常。

练习3:分析 bootloader 进入保护模式的过程。

  1. 为何开启A20,以及如何开启A20

(1) 为何开启A20

关闭A20时,PC系统处于实模式(16位模式)运行状态,在这种状态下软件可访问的物理内存空间不能超过1MB,且无法发挥Intel 80386以上级别的32位CPU的4GB内存管理能力。

通过修改A20地址线开启A20,PC系统可以完成从实模式到保护模式的转换。在保护模式下,80386的全部32根地址线有效,可寻址高达4G字节的线性地址空间和物理地址空间,可访问64TB(有2 ^ 14个段,每个段最大空间为2 ^ 32字节)的逻辑地址空间,可采用分段存储管理机制和分页存储管理机制。这不仅为存储共享和保护提供了硬件支持,而且为实现虚拟存储提供了硬件支持。通过提供4个特权级和完善的特权检查机制,既能实现资源共享又能保证代码数据的安全及任务的隔离。

(2) 如何开启A20

打开A20 Gate的具体步骤大致如下(参考bootasm.S):

1. 等待8042 Input buffer为空;
2. 发送Write 8042 Output Port (P2)命令到8042 Input buffer;
3. 等待8042 Input buffer为空;
4.8042 Output Port(P2)得到字节的第2位置1,然后写入8042 Input buffer;

bootasm.S 中开启A20的步骤:
在这里插入图片描述

  1. 如何初始化 GDT 表

初始化 GDT 表
在这里插入图片描述

  1. 如何使能和进入保护模式

(1) 载入 GDT 表
在这里插入图片描述

(2) 进入保护模式:将 cr0 的标志位置为1
在这里插入图片描述

练习4:分析bootloader加载ELF格式的OS的过程。

  1. bootloader如何读取硬盘扇区的?

bootmain.c 中对应的代码:
在这里插入图片描述
bootloader让CPU进入保护模式后,下一步的工作就是从硬盘上加载并运行OS。考虑到实现的简单性,bootloader的访问硬盘都是LBA模式的PIO(Program IO)方式,即所有的IO操作是通过CPU访问硬盘的IO地址寄存器完成。

一般主板有2个IDE通道,每个通道可以接2个IDE硬盘。访问第一个硬盘的扇区可设置IO地址寄存器0x1f0-0x1f7实现的,具体参数自查。一般第一个IDE通道通过访问IO地址0x1f0-0x1f7来实现,第二个IDE通道通过访问0x170-0x17f实现。每个通道的主从盘的选择通过第6个IO偏移地址寄存器来设置。

读一个扇区的流程(可参看boot/bootmain.c中的readsect函数实现)大致如下:

1. 等待磁盘准备好
2. 发出读取扇区的命令
3. 等待磁盘准备好
4. 把磁盘扇区数据读到指定内存
  1. bootloader是如何加载ELF格式的OS?

bootmain.c 中对应的代码:
在这里插入图片描述

bootloader 加载 ELF 格式的 OS 的过程大致如下:

1. 读取ELF的头部并判断是否是合法的ELF文件
2. 按照描述表将ELF文件中的数据载入内存
3. 根据ELF头部储存的入口信息,找到内核的入口

(参考资料:https://blog.csdn.net/tangyuanzong/article/details/78595854)

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值