一. 前言
在跟踪并学习了一遍Linux内核源码框架之后,又经过了一系列相关书籍的阅读,最终决定沉下心来从零开始写一个属于自己的微型操作系统,大致思路如下:
- 写一个简单的可以启动的操作系统,显示Hello World
- 实现第一个进程
- 实现基本的内存管理
- 实现多进程及进程调度
- 增加设备注册、设备驱动
- 增加虚拟文件系统
- 增加网络架构
- 封装内核态和用户态
- 添加交互,如shell等
那么现在就从零开始,一步一步搭建属于自己的操作系统吧。路可能很漫长,但是只要坚持就一定会有收获。本讲内容为如何写出一个可以启动的操作系统。
二. 实现
在前文研究操作系统的时候提到过,电源启动后会由CS:IP组成指令指向BIOS,随后会跳转至bootloader加载操作系统。由此,我们需要以下几部分内容:
- 一个可运行的程序文件(操作系统)
- 对bootloader的修改
2.1 程序文件
首先看看程序文件,首先我们当然需要一个汇编文件来做初始化以及系统的调用。
1 | ## entry.asm |
以上的汇编代码分为 4 个部分:
- 代码 1~40 行,用汇编定义的 GRUB 的多引导协议头,其实就是一定格式的数据,我们的 Hello OS 是用 GRUB 引导的,当然要遵循 GRUB 的多引导协议标准,让 GRUB 能识别我们的OS。之所以有两个引导头,是为了兼容 GRUB1 和 GRUB2。
- 代码 44~52 行,关掉中断,设定 CPU 的工作模式。
- 代码 54~73 行,初始化 CPU 的寄存器和 C 语言的运行环境。
- 代码 78~87 行,GDT_START 开始的,是 CPU 工作模式所需要的数据。
最后调用main函数作为入口函数启动,函数如下:
1 | // main.c |
其中对应的头文件意义在于定义printf
函数。我们通常使用的printf来自于运行时库,而此处自建操作系统是没有库函数用的,需要自己写一个能够打印并显示出来的函数。自制的 printf
函数直接调用了 _strwrite
函数,而 _strwrite
函数正是将字符串里每个字符依次定入到 0xb8000 地址开始的显存中,而 p_strdst
每次加 2,这也是为了跳过字符的颜色信息的空间。
1 | //vgstr.h |
在前文hello world的研究中,我们有提及通过lds可以自定义链接文件和虚拟内存分配。
1 | ENTRY(_start) |
最后再编写一个makefile
文件来自动编译。注意这里在Makefile
中增加了update_image
及qemu
,用于通过qemu
直接启动而不需要修改操作系统文件。
1 | MAKEFLAGS = -sR |
2.2 bootloader的修改
实际上,在makefile里我们已经做了一个qemu的镜像操作,利用qemu可以不用修改bootloader,直接在Linux或者windows上启动虚拟机,操作如下
1 | make && make umount_image |
如果还是希望能够在Linux/Windows上实际生效一个新的操作系统,那就需要用到下面的知识修改BootLoader添加新的操作系统配置项了。
首先需要修改的是grub.cfg
配置文件,该文件实际存储了各个操作系统配置,会加载并显示在屏幕供选择。
1 | menuentry 'HelloOS' { |
接着修改/etc/default/grub
,修改步骤如下
a. 注释掉HIDDEN所在的2行
b. 将GRUB_TIMEOUT设置为30(使用默认值10其实也可以)
c. 将GRUB_CMDLINE_LINUX_DEFAUL设置为text
执行sudo update-grub
更新
然后将bin文件拷贝至/boot目录下,重启系统即可。