30天自制操作系统
前言
希望通过这本书,建立起对操作系统的整体框架认知。
day 1
编辑二进制文件,使用的是 winhex,就不用作者那个工具了。整体敲下来,也不算很复杂,不过可以偷懒的,直接把作者的整个文件复制即可。但是总得有点参与感是吧,毕竟目前原理不懂,bat 文件也是写好的。



这里似乎是作者遗漏了,看 bat 文件就能明白,在 img 文件所在目录的上级目录中要存在 z_tools,复制过去即可,然后双击 !cons_nt.bat(其实就是在当前目录打开 cmd 窗口),调用 qemu 模拟 img 映像文件,成功输出了 hello,world。

进而用汇编写。

后续的也没什么新的,只是增加了注释。看的时候,感觉增加了不少知识,但是应该不久就会消失吧?hh~
day 2
今天仍然是没什么内容,那就简单记录下我所不知道或者不清晰的知识吧:
- ORG 指令:告诉汇编器从哪个地址开始载入机器码。
- HLT 指令:让 CPU 进入待机状态,当外部发生变化时(例如 IO 设备操作)才会继续执行程序。
- 0x7c00~0x7dff 是一个作为主引导记录(书上称为启动区,但是搜索之后这个才是现在的称呼)的扇区(512 字节,因为计算机读取硬盘区域,一次性就会读取 512 字节,主引导区以 0x55AA 结尾),存放启动程序加载器的区域,用于唤醒操作系统。
然后就是 make 命令,见了挺多回的了(并没有自己写过,但是有依葫芦画瓢修改过),这次做下整理:
make 会默认去寻找名叫
Makefile或者makefile的文件,并且只能是这两个名字。xxx : yyy 前者是要构建出的目标文件,后者是构建前者需要的依赖文件。在这句指令的下一行写入的是需要执行的命令,而这条命令的前面需要四个空格占位(这很关键,不然会报错的)
我们一般也可以指定 以下指令来帮助我们快速删除文件:
1
2
3.PHONY: clean
clean:
rm -f xxx.PHONY 叫做伪目标,这样如果输入
make clean,make 不会去寻找是否存在一个名叫 clean 的文件,而是去执行 clean 指令。变量声明引用通过
$(),看代码:1
2OBJ=xxx yyy zzz
all: $(OBJ)$(OBJ) 表示将 OBJ 的值代入到 all 的依赖文件去,这里也是一个小小的技巧:如果我们只是输入 make 指令,会默认去执行生成第一个文件,然后又因为会检查依赖文件是否存在,从而去生成依赖文件。所以这样编写,就可以只需要输入 make,完成一系列的操作。
day 3
磁头、柱面、扇区:
- 一个磁盘(圆形)分为正反面称为磁头。
- 一个磁头以按照同心圆的方式一圈圈,由外向内的顺序划分出若干个柱面。
- 一个柱面内划分出若干个扇区。
Makefile 改变内容的语法规则已在 day 2 说明,不再赘述了。
小小总结一下从书上得来的知识:
- 操作系统是设备一通电就能运行的一段程序(个人理解)
- BIOS 会读取磁盘的第一个扇区作为主引导记录,所以要在这个扇区里写入启动程序加载器。
- 启动程序加载器会被从磁盘读取到内存的 0x7c00~7dff 区域,然后在内存中执行,从而再将磁盘后续存储的操作系统读入到内存的中以启动操作系统,之后把操作权限给予操作系统。
加载操作系统,跟上面的总结理解是一致的。处理十分直接,通过先定位加载的地址,然后使用 jmp 进行跳转。如果使用 winhex 处理的话,直接帮我们区分好了,很方便。



运行之后,也是如愿的一片漆黑的画面。

为了能够使用 C 语言进行编写,后续作者进行了一些处理,内联的具体操作不是很懂,跟熟知的编译流程有些不大一样,很多不认识的中间文件,但是作者也并未细说,就也不细究了,毕竟我的目的只是为了了解整个操作系统的框架,那今天就结束吧。
day 4
主要是通过 BIOS 调用了显示器进行显示颜色,并且在汇编中已经指定了哪块内存是显存,之后也只要把需要显示的内容存放进显存,就会被显示出来。
虽然书上没办法显示,但是我的博客里是可以看出来的,因为显存换成了白色的颜色,所以呈现了白屏。因为我的能显示,那条纹的就不放上来了,hhh

今天主要是介绍了一些 C 语言语法,新得的知识应该是:如果想要呈现一个图画,需要一个个像素点进行着色,然后再进行一定的排序,而如果要显示出任务栏这样的东西,则是使用深浅变化的颜色体现出层次感,那么接下来把最后的两张图放一下就过了吧~

确实是有了点早期 windows 桌面的既视感。

day 5
显示的字符按照最基础的像素点进行排布,例如构成 A 的像素点是需要着色的,而其他的点是无需着色的(显示一个 A,是需要在一个正方形内部进行设计的),做完之后放进显存,即可显示。

因此若是要设计出字体,如下图一般进行个性化的摆放设计,这样导入显存时就会呈现出来,当然下图是作者用一种字体,但是其他人是如何将这种形式转换成机器码就看每个人自身了。

因为是有工具可以处理上述的字体,将其与其他的目标文件相链接,所以作者可以在 C 语言中通过 extern 访问到。


接下来是将其封装为一个函数,提高复用时的效率问题以及调用了 sprintf 进行打印变量值的处理,就不放图片了,没什么改变的。不过作者还是再三的强调了除了 sprintf 外的 printf 家族都会调用操作系统的功能,可以解读出来,我们通常见到的 stdout、stdin、stderr 都是鉴于操作系统进行定义的。而只有对内存的操作才是最本质的(所有程序运行的根本条件就是存在了内存的概念),可以很容易的对其进行操作。
制作鼠标这个点,hhh,让人恍然大悟:原来这些东西都是这么来的。看这个循环就很能体会到了,大的东西拆解开的时候,本质都是简单(我随便说说的):用一些符号去描述出鼠标的形状,然后对其循环遍历,对每个位填充上不同的颜色,鼠标的形状之外(因为是要在一个正方形内去设计)显出背景的底色,看看自己的鼠标,这很合理。

跟上面那些符号构成的图案是一致的,只是下面有进行上色。

最后是让鼠标动起来,可惜今天还没实现,但是有新增的知识:
- 首先是分段,32 位系统仍然需要段寄存器指定起始地址,但是段寄存器只有13 位可写,因此用这 13 位去保存 0~8191 的段号,而每个段号对应的段起始地址则存放在某段内存中,这些地址数据(8192 * 8 = 65536 = 64KB)被称作 GDT(global (segment) descriptor table 全局段号记录表)。而存放段起始地址的内存地址以及有效个数则被存放在 GDTR 寄存器中。
- IDT(interrupt descriptor table 中断记录表)记录了 0~255 的中断号码与调用函数的对应关系。中断的是一种十分正常的机制,用于去处理硬件(外部中断)与软件(内部中断)的输入输出处理。
代码部分则是多了对于 GDT 和 IDT 的初始化,确立好他们在内存的位置
day 6
- 本文作者:ShouCheng
- 本文链接:http://shoucheng3.github.io/2023/03/03/2023-03-03-30%E5%A4%A9%E8%87%AA%E5%88%B6%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F/index.html
- 版权声明:本博客所有文章均采用 BY-NC-SA 许可协议,转载请注明出处!