Hello OS

操做系统对于每一个开发者来讲都是绕不开的门槛,不论是传统的单片机也好,仍是如今分布式系统也好,都是离不开基本是计算机模型,从图灵机到冯诺依曼,从埃尼阿克到如今太湖之光,这几十年来的计算机发展都仍是在这个模型下发展起来的,能够说在量子计算机大规模推广以前,现今的操做系统软件仍是很值得学习借鉴。俗话说,它山之石能够攻玉,那么咱们本身磨石头,或许也能够发现蕴含在石头中的璞玉,这也是一件很值得期待的事情呢,不是吗?docker

最近就想本身动手实验本身写个操做系统,本文权且做为本系列做品的开篇之做,按照我对操做系统的认知来层层推动,最终的指望固然是本身写出个性化的操做系统啦,有机会的话,再继续深刻到分布式操做系统,进而进入云操做系统,想一想也是挺刺激的,试试看呗,看看能作到多少~网络上资料这么多,牛人这么强,应该能够啦编程

OS以前

Hello OS以前,先要搞清楚所谓的操做系统在上电以后的引导流程,总结来讲以下图所示:网络

Hello OS

简单来讲PC机的BIOS固件是一种已经固化在PC机主板上的 ROM芯片中的操做系统,即便掉电也能保存,而PC机上电后的第一条指令就是在BIOS固件中的,它负责检测和初始化 CPU、内存及主板平台,而后加载引导设备(大几率是硬盘)中的第一个扇区数据,到0x7c00地址开始的内存空间,再接着跳转到0x7c00 处执行指令,其实就是执行GRUB引导程序。分布式

此次实验是用来体验一下本身编译一个.bin文件,而后修改的Ubuntu引导程序,进而启动编译完成的这个系统文件。ide

Hello OS引导的汇编代码

知道PC机的上电流程以后,就能够开始进行逐步开发了,好比说利用一下汇编语言来进行引导程序的开发entry.asm函数

MBT_HDR_FLAGS EQU 0x00010003
MBT_HDR_MAGIC EQU 0x1BADB002 ;多引导协议头魔数
MBT_HDR2_MAGIC EQU 0xe85250d6 ;第二版多引导协议头魔数
global _start ;导出_start符号
extern main ;导入外部的main函数符号
[section .start.text] ;定义.start.text代码节
[bits 32] ;汇编成32位代码
_start:
jmp _entry
ALIGN 8
mbt_hdr:
dd MBT_HDR_MAGIC
dd MBT_HDR_FLAGS
dd -(MBT_HDR_MAGIC+MBT_HDR_FLAGS)
dd mbt_hdr
dd _start
dd 0
dd 0
dd _entry
;以上是GRUB所须要的头
ALIGN 8
mbt2_hdr:
DD MBT_HDR2_MAGIC
DD 0
DD mbt2_hdr_end - mbt2_hdr
DD -(MBT_HDR2_MAGIC + 0 + (mbt2_hdr_end - mbt2_hdr))
DW 2, 0
DD 24
DD mbt2_hdr
DD _start
DD 0
DD 0
DW 3, 0
DD 12
DD _entry
DD 0
DW 0, 0
DD 8
mbt2_hdr_end:
;以上是GRUB2所须要的头
;包含两个头是为了同时兼容GRUB、GRUB2
ALIGN 8
_entry:
;关中断
cli
;关不可屏蔽中断
in al, 0x70
or al, 0x80
out 0x70,al
;从新加载GDT
lgdt [GDT_PTR]
jmp dword 0x8 :_32bits_mode
_32bits_mode:
;下面初始化C语言可能会用到的寄存器
mov ax, 0x10
mov ds, ax
mov ss, ax
mov es, ax
mov fs, ax
mov gs, ax
xor eax,eax
xor ebx,ebx
xor ecx,ecx
xor edx,edx
xor edi,edi
xor esi,esi
xor ebp,ebp
xor esp,esp
;初始化栈,C语言须要栈才能工做
mov esp,0x9000
;调用C语言函数main
call main
;让CPU中止执行指令
halt_step:
halt
jmp halt_step
GDT_START:
knull_dsc: dq 0
kcode_dsc: dq 0x00cf9e000000ffff
kdata_dsc: dq 0x00cf92000000ffff
k16cd_dsc: dq 0x00009e000000ffff
k16da_dsc: dq 0x000092000000ffff
GDT_END:
GDT_PTR:
GDTLEN dw GDT_END-GDT_START-1
GDTBASE dd GDT_START

以上的汇编代码分为 4 个部分:工具

  1. 代码 1~40 行,用汇编定义的GRUB的多引导协议头,其实就是必定格式的数据,Hello OS 是用 GRUB 引导的,固然要遵循GRUB多引导协议标准,让 GRUB 能识别的 Hello OS。而之因此有两个引导头,是为了兼容 GRUB1 和 GRUB2。
  2. 代码 44~52 行,关掉中断,设定 CPU 的工做模式。
  3. 代码 54~73 行,初始化 CPU 的寄存器和 C 语言的运行环境。
  4. 代码 78~87 行,GDT_START开始的,是 CPU 工做模式所须要的数据

主函数

上面的汇编代码调用了main函数,而在其代码中并无看到其函数体,而是从外部引入了一个符号。那是由于这个函数是用 C 语言写的在main.c中,最终它们分别由nasmGCC 编译成可连接模块,由LD 连接器连接在一块儿,造成可执行的程序文件:学习

#include "vgastr.h"
void main(){ 
    printf("Hello OS!"); 
}

这里用到的printf也不是咱们熟知的那个函数,只是碰巧名字同样罢了,这个显示函数是须要咱们本身实现的。调皮一些的话,printf还能够改为echoshowkankanwo这些,不要紧的,毕竟这个函数也是咱们本身定义的。测试

控制计算机屏幕

咱们之因此能够看到的屏幕显示的内容,是由于有个硬件的来支撑的,即咱们常说的显卡。若是咱们要在屏幕上显示字符,本质上就是编程操做显卡。这个并不难,作完了甚至还挺有成就感。操作系统

注意到不管咱们 PC 上是什么显卡,它们都支持一种叫VESA的标准,这种标准下有两种工做模式:字符模式和图形模式。显卡们为了兼容这种标准,不得不本身提供一种叫VGABIOS 的固件程序。

这里须要补充一下在上古时代显卡的字符模式的工做细节。它把屏幕分红 24 行,每行 80 个字符,把这(24*80)个位置映射到以0xb8000地址开始的内存中,每两个字节对应一个字符,其中一个字节是字符的ASCII码,另外一个字节为字符的颜色值。以下图所示:

Hello OS

了解细节以后就能够对显示程序vgastr.c进行开发

void _strwrite(char* string)
{
    char* p_strdst = (char*)(0xb8000);
    while (*string)
    {

        *p_strdst = *string++;
        p_strdst += 2;
    }
    return;
}

void printf(char* fmt, ...)
{
    _strwrite(fmt);
    return;
}

代码很简单,printf函数直接调用了_strwrite 函数,而_strwrite函数正是将字符串里每一个字符依次找到以0xb8000 地址开始的显存中,而p_strdst每次加2,则是为了跳过字符的颜色信息的空间。

编译和安装

Hello OS 的代码都已经写好,这时就要进入安装测试环节了。不过在安装以前,还要进行系统编译,即把每一个代码模块编译最后连接成可执行的二进制文件。

make

make 历史悠久,小巧方便,也是不少成熟操做系统编译所使用的构建工具。

咱们在软件开发中,make是一个工具程序,它读取一个叫makefile的文件,也是一种文本文件,这个文件中写好了构建软件的规则,它能根据这些规则自动化构建软件,就相似咱们用docker打包的时候,须要写dockerfile同样。

任何一个 Linux 发行版中都默认自带这个 make 程序,因此不须要额外的安装工做,咱们直接使用便可。

编译

下面咱们用一张图来描述咱们 Hello OS 的编译过程,以下所示

Hello OS

安装 Hello OS

通过上述流程,能够获得Hello OS.bin文件,可是还要让GRUB可以找到它,才能在计算机启动时加载它。这个过程称为安装,不过这里没有写安装程序,得咱们手动来作。经研究发现,GRUB 在启动时会加载一个grub.cfg 的文本文件,根据其中的内容执行相应的操做,其中一部份内容就是启动项。GRUB 首先会显示启动项到屏幕,而后让咱们选择启动项,最后 GRUB 根据启动项对应的信息,加载 OS 文件到内存。

menuentry 'HelloOS' { 
        insmod part_msdos #GRUB加载分区模块识别分区 
        insmod ext2 #GRUB加载ext文件系统模块识别ext文件系统 
        set root='hd0,msdos1' #注意boot目录挂载的分区,这是我机器上的状况 
        multiboot2 /boot/HelloOS.bin #GRUB以multiboot2协议加载HelloOS.bin 
        boot #GRUB启动HelloOS.bin
}

关于root的配置状况,若是不知道本身boot分区的挂载点,能够在grub的引导程序上面按C

Hello OS

进入GRUB的命令行,而后查看提供的挂载分区,这回没有巧办法了,只能一步步调试了,改挂载的分区点,最重要的是,记得把make以后生成的HelloOS.bin文件拷贝到\boot目录下

Hello OS

以后就正常启动吧:)完成本次实验

Hello OS

总结一下

此次实验先从按下 PC 机电源开关开始,窥探了PC 机的引导过程。它从 CPU 上电,到加载 BIOS 固件,再由 BIOS 固件对计算机进行自检和默认的初始化,并加载 GRUB 引导程序,最后由 GRUB 加载具体的操做系统。其次,用汇编语言和 C 语言实现 Hello OS。第一步,用汇编程序初始化 CPU 的寄存器、设置 CPU 的工做模式和栈,最重要的是加入了 GRUB 引导协议头;第二步,切换到 C 语言,用 C 语言写好了主函数和控制显卡输出的函数,这个时候还须要了解显卡的一些工做细节。最后,就是编译和安装 Hello OS 了。我用了 make 工具编译整个代码,其实 make 会根据一些规则调用具体的 nasm、gcc、ld 等编译器,而后造成 Hello OS.bin 文件,最后把这个文件写复制到 boot 分区,写好 GRUB 启动项,这样就行了。

嗯,期待下次更深刻的探索O(∩_∩)O

相关文章
相关标签/搜索