获取完资源就开始干了。
我这里就做了个大概的内容分析,具体的还是跟着那几个大佬吧
前面一大堆都是没用的,就是告诉一些作业的提交。
Part 1: PC Bootstrap
这个并没有要你做啥,就是让你 熟悉下汇编。
然后让你知道怎么运行那个内核的,这些都是些不用讲的东西,看看就行了。
说一下,这个内核退出说是ctrl+a x
,意思是先按ctrl+a
,再按x
然后就开始来重点了,第一个是让你知道现在计算机内存的分布。
The PC’s Physical Address Space(PC的物理地址空间)
The ROM BIOS
后面就比较直接了,直接让你去运行这个内核,一步步来看他是怎么运行的。
就是让你这样运行两个终端,跑GDB 调试,第一个运行make qemu-gdb
,第二个运行make gdb
。
不出意外,在make gdb
里面出现的第一条指令是跳转指令。
跳到了哪里去,这个地址官方是给了你解释的,就是段地址*16加偏移地纸,很显然,他的地址是在BIOS中。
然后回让你一步步执行看发生了什么。
不难发现,计算机的运行最先开始的是 BIOS。
后面一步步,执行的内容我就看不懂,只知道他就做了一些初始化工作,想了解的去看看大佬的博客吧。
Part 2: The Boot Loader
初始化完成BIOS 之后,就运行Boot Loader,就是引导操作系统,这个东西一直都是放在计算机磁盘的第一个扇区(也用可能是其他的引导,这个百度搜一搜能搜出来,另外一个最常见的就是U盘启动,学计算机的重装系统至少也有十次八次了吧,所以这个很容易理解,我们重装系统的步骤不就是现在BIOS里面选了U盘引导,干的就是这个了)。然后BIOS会把这个运行的引导程序装入到内存0x7c00-0x7dff
。我不知道现代操作系统是不是这,但是这个系统是的。
这个引导系统主要就干了两件事:
- 实模式转换成保护模式,区别百度一下有讲解的,另外发现了另一个操作系统学习资源,好像文档还挺齐全的。
- 引导加载程序通过x86的特殊I / O指令直接访问IDE磁盘设备寄存器,从而从硬盘读取内核。
后面会让你去看源码,知道怎么运行的Boot Loader,这个各位就自己翻翻博客吧,我就不细讲了,贴个自己的大致介绍。
1 |
|
由上面这注释,应该很容易得出他问的问题的答案。
后面有一个让你运行一个C语言指针程序,那个有点基础应该就看得懂。就不多说了。
main.c里面,主要是加载ELF文件,也就是你的操作系统,什么是ELF,这个可以在百度百科里面了解到。
boot main.c
1 |
|
后面几步还是要自己好好坐一坐,我没有做什么详细的介绍。
一如既往大佬博客,像我这样的菜鸡是无法理解的。
最后让你做个测试,运行Boot Loader 前看下0x00100000
处的8个内存字,运行后再看一下,发现原本是 全是 0
,后面就有了一大堆乱七八糟的值。这个不是很明显吗,看我上面main.c
的注释,明显有个函数把硬盘里面的值读到了内存。
Part 3: The Kernel
后面的实验,这个大佬的GitHub就全都有了。
我做个简单的笔记。
1 | movl %eax, %cr0 |
这条语句实现了,映射,将高地址映射到了物理地址的低地址,这是因为计算机希望操作系统是运行在高地址,用户运行在低地址,详细内容,下一次实验会讲清楚。
练习8 让你完善那个printf
里面的%o
也就是8进制输出。
这个很容易了,把前面16进制输出复制一遍就行了,不做详细介绍了。
然后你需要回答4个问题
printf
之间的关系依旧找大佬的博客1
2
3
4
5
6
71 if (crt_pos >= CRT_SIZE) {
2 int i;
3 memmove(crt_buf, crt_buf + CRT_COLS, (CRT_SIZE - CRT_COLS) * sizeof(uint16_t));
4 for (i = CRT_SIZE - CRT_COLS; i < CRT_SIZE; i++)
5 crt_buf[i] = 0x0700 | ' ';
6 crt_pos -= CRT_COLS;
7 }
解释这个,看起来就是如果超过了一页,就把最前面的那一行删掉??,然后继续输出下一行???不知道理解对没。
- 这个是个重点 ,这个实验很容易看出来C语言压栈的顺序是从后往前压栈。
- 这个不说了,就是按格式输出
- 这个就是,我们经常用的
printf
,如果少了一个参数会咋样,通过看内存应该就知道,会输出一个随机值。多一个多的其实就没啥用。 - 这个就是压栈是从后往前压,所以可以实现,多个参数。如果是从前往后压该怎么办?我还不知道怎么处理。
练习9 问啥时候初始化堆栈,这个看看大佬的博客就行.
练习10 这个就是让你明白是怎么调用函数的,又是怎么返回的。
一般来说,栈基地址指向的是栈底,栈指针指向的是栈顶。
大概调用一个函数就是这么干的,怎么返回的就不用我说了吧。
调用参数也就是通过栈来的,如果超过了五个参数我也不知道会发生啥,反正这上面没说。
练习 11 也就是让你写个输出栈里面的内容。
练习 12 让你实现一个调试信息的指令。
改三个地方。
前两个都在kern/monitor.c
。
第一个
1 | static struct Command commands[] = { |
后面那个是多加的,前面那两个很眼熟吧。
1 | int |
这个也很眼熟吧,前面改过。
到这个地方实际上已经能够运行了,只是没有行号。
最后一个在kern/kdebug.c
1 | stab_binsearch(stabs, &lfun, &rfun, N_SLINE, addr - info->eip_fn_add r); |
这个具体原理我也不知道,抄别人的。百度能搜到。
到此,实验一就全部完成了,目前我能了解的就这么多。