编写一个引导程序
首先我们要先要编写一个汇编程序,使用C语言作为高级语言不能直接控制硬件,而且 C 语言的函数调用、函数传参,都需要用栈。我们需要先要为C语言提供一个工作环境。
MBT_HDR_FLAGS EQU 0x00010003;flag字段,指出OS映像需要引导程序提供或支持的特性 |
我们来对上面的汇编做一些详细的解释:
我们先来看代码的第1行到第39行
首先是MBT_HDR_FLAGS,其定义的是flags字段,用来指出OS映像需要引导程序提供或支持的特性。其中0-15位指出需求:如果引导程序发现某些值杯设置,但出于某种原因无法满足其需求,则需要告知用户,并拒绝加载操作系统映像。其中16-31位为可选特性,与低16位不同,如果无法满足要求可以忽视并照常进行。自然,所有flags尚未定义的位都必须设置为0。flags字段的作用是用于版本控制以及简单的功能选择。
其中第0位若为1,那么所有与操作系统一起加载的引导模块必须在页面(4KB)边界上对齐。有些操作系统能够在启动时将包含引导模块的页直接映射到一个分页的地址空间,因此需要引导模块是页对齐的。
如果第1位为1则必须通过Multiboot信息结构的**mem_***域包括可用内存的信息。
如果引导程序能够传递内存分布(**mmap_***域)并且它确实存在,则也包括它。
如果第2位为1,有关视频模式表(参见引导信息格式)的信息必须对内核有效
如果flags字段中的第16位被设置,那么Multiboot头部中偏移量为12-28的字段是有效的,引导加载器应该使用它们而不是实际可执行头部中的字段来计算加载操作系统镜像的位置。如果内核镜像是ELF格式的,那么这个信息不需要被提供,但是如果镜像是a.out格式或者其他格式的,那么它必须被提供。符合规范的引导加载器必须能够加载那些要么是ELF格式的,要么包含了嵌入在Multiboot头部中的加载地址信息的镜像;它们也可以直接支持其他可执行格式,比如特定的a.out变体,但是不是必须的。
接下来MBT_HDR_MAGIC则代表着多引导协议头魔数,MBT_HDR2_MAGIC第二版多引导协议头魔数,可以告诉计算机使用的是何种协议。
接下来从第10行到第19行代表GRUB所需要的头,21到39行为GRUB2所需要的头。包含这两个头是为了兼容GRUB与GRUB2。
代码43~5行,关掉中断,设定 CPU 的工作模式。
代码53~72行,初始化 CPU 的寄存器和 C 语言的运行环境。
代码77~86行,GDT_START 开始的,是 CPU 工作模式所需要的数据。
编写C代码
//main.c |
主函数实现了一个打印”Hello OS!”字符串的功能,但是我们的操作系统中没有库函数,所以需要我们自定义一个printf函数打印到屏幕上。
//vgastr.c |
_strwrite函数将我们要打印的字符打印到屏幕之上从0xb8000开始每两个字符代表一个字母,第一个字节代表着字符,第二个字节代表着字体的颜色,其字符编码通常是 utf8,而 utf8 编码对 ASCII 字符是兼容的。
//vgastr.h |
编译和部署
通过makefile将代码编译,得到HelloOS.bin。
接下来就是部署了
我们进入etc\default\grub文件
首先将GRUB_TIMEOUT_STYLE的值从hidden改为menu,并将GRUB_TIMEOUT的值从0改为30
然后更新grub配置
sudo update-grub |
然后我们修改/boot/grub/grub.cfg文件,增加HelloOS项
menuentry 'HelloOS' { |
根据df /root/
的命令来查看set root应该设置为什么
其中的“sda3”就是硬盘的第四个分区(硬件分区选择 MBR),但是 GRUB 的 menuentry 中不能写 sda3,而是要写“hd0,gpt3”,这是 GRUB 的命名方式,hd0 表示第一块硬盘,我的虚拟机是使用gpt分区表所以是gpt3。
可以根据grub.cfg其他的menuentry来确定。
最后将HelloOS.bin文件复制到/boot/目录下,重启虚拟机。
选择HelloOS,可以看到HelloOS的字样。
这样我们就实现了一个最简单的OS了!