X86架构操作系统内核实现过程

#操作系统内核实现(一)
操作系统的学习还是需要落地不能停留在概念上,于是打算模仿并实现操作系统内核,前面一直在做川合秀实先生的《30天自制操作系统》,但是由于它的底层系统是windows,以及讲解较为浅显不能深入了解具体的内核运行模式状态,转到了刘欢师兄写的《X86架构操作系统内核实现》上,博客虽短只有10篇左右,但内容充实,不断的带入新的文章新的链接,在做内核的途中也游览了许多计算机各方面知识的风景,收益颇丰。
###开发工具
本次开发使用的都是常见且基础的工具

  • 开发语言为少量的汇编,大部分为C
  • 汇编编译器选择nasm
  • C编译器采用gcc
  • 链接器ld
  • 虚拟机当然是linux下常用的qemu

###开发过程
做事情之前首先要明确我们具体要做什么,我们要做的是一个从0开始的操作系统,从0指的是从通电的一瞬间开始,首先我们必须要让计算机能够加载我们所做的OS,那么我们不得不了解计算机的具体启动过程。
下面这张图是我通过阅读《计算机是如何启动的》画出的。
计算机启动过程

  • 这张图对计算机的启动过程描述的十分详细了,细微的地方不再讲解,我们的重心是做出一个OS,但有一点我们需要注意,当BIOS读取设备第一扇区后,除了常规的直接加载Bootloader外,我们可以选择使用GRUB(来自GNU的多操作系统启动程序)来直接载入操作系统内核,因为写Bootloader需要太多的硬件知识偏离了我们的重心,同时自己动手实现Bootloader会造成与已有OS的不兼容。
  • GRUB提供了multiboot规范,符合这个协议的操作系统内核能被GRUB识别,并按照定义的规则被加载。
    ####开发过程中所用到的脚本文件
    开发过程中我们需要使用脚本文件来帮助进行编译链接
  • 首先是我们熟悉的Makefile,我们需要它来编译我们的整个内核,而这个Makefile在整个开发过程中几乎可以全程使用,来看下刘欢师兄所写的Makefile脚本。
#!Makefile

C_SOURCES = $(shell find . -name "*.c")
C_OBJECTS = $(patsubst %.c, %.o, $(C_SOURCES))
S_SOURCES = $(shell find . -name "*.s")
S_OBJECTS = $(patsubst %.s, %.o, $(S_SOURCES))

CC = gcc
LD = ld
ASM = nasm

C_FLAGS = -c -Wall -m32 -ggdb -gstabs+ -nostdinc -fno-builtin -fno-stack-protector -I include
LD_FLAGS = -T scripts/kernel.ld -m elf_i386 -nostdlib
ASM_FLAGS = -f elf -g -F stabs

all: $(S_OBJECTS) $(C_OBJECTS) link update_image

.c.o:
    @echo 编译代码文件 $< ...
    $(CC) $(C_FLAGS) $< -o $@

.s.o:
    @echo 编译汇编文件 $< ...
    $(ASM) $(ASM_FLAGS) $<

link:
    @echo 链接内核文件...
    $(LD) $(LD_FLAGS) $(S_OBJECTS) $(C_OBJECTS) -o hx_kernel

.PHONY:clean
clean:
    $(RM) $(S_OBJECTS) $(C_OBJECTS) hx_kernel

.PHONY:update_image
update_image:
    sudo mount floppy.img /mnt/kernel
    sudo cp hx_kernel /mnt/kernel/hx_kernel
    sleep 1
    sudo umount /mnt/kernel

.PHONY:mount_image
mount_image:
    sudo mount floppy.img /mnt/kernel

.PHONY:umount_image
umount_image:
    sudo umount /mnt/kernel

.PHONY:qemu
qemu:
    qemu -fda floppy.img -boot a

.PHONY:bochs
bochs:
    bochs -f tools/bochsrc.txt

.PHONY:debug
debug:
    qemu -S -s -fda floppy.img -boot a &
    sleep 1

前面一部分是一些shell所写的定位目标文件,以及gcc与ld的配置,值得一提的是ld的链接命令中-nostdlib 是不链接C语言的标准库,因为我们目前并没有函数库,否则也不能称之为一个从0的操作系统了,在看代码的过程中碰到了patsubst这个函数,这里它是把.c与.s文件转换成了.o文件,那为什么可以直接转换呢?变成.o文件难道不需要编译了吗?Google了一下发现他是替换通配符,即只替换后缀,我觉的在这里把. s与.c文件都换成.o文件应该是想划定范围,用范围内的这些项目链接挂载在kernel下的文件把。最后是一些文件挂载和虚拟机qemu的镜像制作等。我们应该能注意到.PHONY这个命令,这个命令的意思是告诉Make,总是要执行它后面的命令,也就是说始终认为它后面生成的文件没有更新需要生成最新的。

  • 接下来是ld链接器的脚本。这个脚本主要是告诉ld程序如何构造我们所需的内核映像文件,此脚本暂时难以搞懂,我们明白了他的作用就好,其中部分需要在之后的调试信息中才能明白,我们先向下接着走。
  • 小结:所以我们现在要做的是使用Makefile与ld编译链接我们的内核文件,接着使用qemu生成镜像文件,按照Multiboot生成规范格式的引导信息,同时使用标准ELF格式,GRUB就会将我们所写的内核正确加载与执行。
    当前我们的目录结构是这样:
|-- Makefile
`-- scripts
    `-- kernel.ld

####启动镜像
打算在一个虚拟软盘上放入我们的内核,刘欢师兄博客中写到给出了软盘镜像但并没有找到,所以准备自己先尝试做一做带有GRUB的软盘映像。先明确一下步骤,制作软盘,接着在这个软盘上安装GRUB。

  • 制作软盘在linux下比较简单,使用dd命令,我们首先创建两个软盘镜像。
#dd if=/dev/zero of=auxiflp.img bs=512 count=2880;
#dd if=/dev/zero of=bootflp.img bs=512 count=2880;

接着从ftp://alpha.gnu.org/gnu/grub/下载GRUB的编译好的文件,当然也可以选择下源文件自己编译后再用。解压后将stage1,stage2提取出来用,刚才创建的两个软盘镜像auxiflp.img用来安装GRUB文件,bootflp。img是我们最终要得到的引导盘,接着我们要把stage1,stage2写入auxiflp.img中。

#dd if=stage1 of=auxiflp.img bs=512 count=1;
#dd if=stage2 of=bootflp.img bs=512 seek=1; 

这样就完成了auxiflp.img。接着我们对bootflp.img进行操作,首先把它格式化为我们常见的FAT12格式,新建文件夹用来挂载软盘映像等。

#mkfs.vfat -F12 bootflp.img       ;
#mkdir floppy                ;
#mount -o loop bootflp.img floppy  ;    
#mkdir -p floppy/boot/grub      
#cp stage* floppy/boot/grub      ;

我们现在明确auxiflp.img中装好了GRUB,bootflp.img已经做好了一切准备,接下来的工作是用auxiflp帮助bootflp.img完成GRUB的安装,由于对qemu的使用不熟练,此处还在不断尝试中。

  • 完成了含有GRUB的软盘制作后,我们可以说做好了所有的铺垫工作,接下来的就是内核的代码工作了。

猜你喜欢

转载自blog.csdn.net/weixin_43122409/article/details/82831275