内核是操作系统最核心的内容,主要提供硬件抽象层、磁盘及文件系统控制、多任务等功能,由于其涉及非常广泛的计算机知识,很少被人们所熟悉,因而披上了一层神秘的面纱。 本文将从零开始实现一个最简单的内核,其可以通过x86系统的GRUB引导启动,并向屏幕输出“Hello World!“字符串。该内核代码非常简短,并且在本人的Debian 7系统中可以正常运行。 x86机器启动过程在具体实现这个内核之前,我们先看看机器具体是怎么启动并且把控制权交给内核的。 x86的CPU固定地在物理地址为[0xFFFFFFF0]的地方开始运行,这是32位地址空间的最后16个字节。这里只包含了一个跳转指令,跳转到BIOS把它自己拷贝到的内存区域的地址。 然后,BIOS开始执行。它首先根据配置的设备启动顺序依次寻找可启动的设备(根据一个特定的魔数可以决定一个设备是否启动)。一旦找到一个可启动的设备,它就把该设备第一个扇区的内容复制到RAM中物理地址从[0x7C00]开始的地方,然后跳转到该地址并且开始执行那里加载的代码。这段代码称为启动引导装载程序(bootloader)。Bootloader然后在物理地址为[0x100000]的地方加载内核,地址[0x100000]就是x86机器上内核的起始地址。 需要的工具
汇编入口点我们希望用C来写所有的代码,但免不了要写一点汇编代码。我们会写一个x86汇编语言的小文件来作为内核的起始点,这段汇编所做的事情就是调用一个我们用C写的外部函数,然后停止程序运行。
怎么确定这段汇编代码会作为内核的起始点呢? 以下是汇编代码:
第一条指令中的bit 32不是x86汇编指令,而是NASM汇编器的伪指令,表明将会产生一段运行在32位处理器上代码。这句代码不是必须的,但显示加上会是一个好的编程实践。
第二行开始就是代码段,即放置代码的地方。 于是,我们有了start函数,它会调用kmain函数,然后通过hlt指令停止CPU。由于中断会从hlt指令中唤醒CPU,所以我们事先使用cli(意为clear interrupts)指令禁止中断。 C语言内核我们在kernel.asm中调用kmain()函数,所以C代码会从kmain()开始执行。
这里内核所做的事情就是:清空屏幕,打印字符串“Hello World!”。 首先是指针vidptr指向地址[0xb8000],这是保护模式下显存的开始地址。屏幕的文本内存只是地址空间的一连串内存区域,它从[0xb8000]开始映射屏幕的输入输出,支持25行,每行80个ASCII字符,每个字符用16位(2字节)表示,而不是我们熟悉的8位(1字节)。2字节中第1个字节是该字符的ASCII表示,第2个字节是属性字节,描述字符的包括颜色在内的属性。如果要让背景为黑色而字体为绿色,可以在第1个字节保存字符的ASCII值,在第2个字节保存值[0x02]:0代表黑色背景,2代表绿色前景。 其它颜色属性定义如下:
我们的内核使用了黑色背景以及灰色字体,所以属性字节为[0x07]。
在第一个while循环中,程序在所有的25行80列中写入空字符和[0x07]属性,从而清空了屏幕。 链接部分
使用NASM把kernel.asm编译成目标文件,再使用GCC把kernel.c编译成另一个目标文件,然后需要把这两个目标文件链接成一个可以启动的内核映像。
OUTPUT_FORMAT设置输出的可执行文件为32位的ELF文件,ELF是x86架构上类Unix系统的标准二进制文件格式。 ENTRY接受一个参数,指定其为最终可执行文件的入口点。
SECTION是这里最关键的部分,它指定不同的段怎么合并以及放在什么地方,从而定义最终可执行文件的布局。 Grub和多重引导现在已经准备好了构建内核的所有文件了,但要用GRUB进行引导还需要最后一个步骤。 多重引导规范(Multiboot specification)是一个使用bootloader加载不同X86内核的标准,GRUB只会加载满足这个规范的内核。根据这个规范,内核必须在它的前8KB字节中包含头信息(Multiboot header)。这个头信息包含4字节对齐的3个域,分别为:
于是kernel.asm应该修改为,代码中的dd表示定义一个4字节的双字:
构建内核现在可以从kernel.asm和kernel.c生成目标文件,然后使用连接脚本进行链接。 使用汇编器nasm产生ELF-32格式的目标文件kasm.o:
使用编译器gcc产生目标文件kc.o,”-c”参数保证只编译,不进行链接:
使用链接器ld根据链接控制脚本产生可执行映像文件kernel:
配置GRUB并运行内核GRUB需要内核以kernel-<version>形式命名,于是把内核kernel重命名为kernel-701,并利用超级管理员权限放到/boot目录下。 对于bootloader为GRUB的发行版,修改配置文件/boot/grub/grub.cfg,添加以下条目:
对于bootloader为GRUB2的发行版,添加的配置应该为:
重启电脑,选择GRUB列表中新增加的kernel-701内核选项,这时可以看到屏幕上显示”Hello World!“。 |