手写简单操作系统内核

本文阅读 5 分钟
首页 Linux,系统 正文

img

本文目的在于自定义一个简易操作系统,通过引导程序启动后打印一行字符串,先演示操作,最后介绍原理。

我的环境:   在virtualBox上面先安装好虚拟机Ubuntu 16.04

源码:    https://gitee.com/hnuwjw/os-hello-os

1.1 设置grub进入引导菜单

  1. 安装nasm,才能执行make

先安装nasm:

sudo apt-get install -y nasm

然后在HelloOS目录下执行:

make -f Makefile

就可以得到HelloOS.bin文件了 img

  1. 配置下启动项,不然无法选择HelloOS菜单

先修改文件:

/etc/default/grub

img

如果修改的时候报错“readonly option is set XXXXX”,则使用超级管理员角色编辑文件:

sudo vim grub

然后执行:

sudo update-grub

1.2 增加HelloOS启动选项

修改/boot/grub/grub.cfg,增加HelloOS启动项:

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
}

img

img

文件系统          1K-块          已用           可用      已用%     挂载点
/dev/sda4      48752308     8087584     38158536    18%          /

其中的“sda1”就是硬盘的第四个分区,但是GRUB的menuentry中不能写sda4,而是要写“hd0,msdos1”,这是GRUB的命名方式,hd0表示第一块硬盘。

然后把HelloOS.bin文件复制到/boot/目录下,最后重启计算机

只要我们的PC机上安装了Ubuntu Linux操作系统,GRUB就已经存在了,就不用我们从引导程序开始写了。

img img

2.1 Hello OS的引导流程

img

PC机BIOS固件是固化在PC机主板上的ROM芯片中的,掉电也能保存,PC机上电后的第一条指令就是BIOS固件中的,它负责检测和初始化CPU、内存及主板平台,然后加载硬盘中的第一个扇区数据,到0x7c00地址开始的内存空间,再接着跳转到0x7c00处执行指令,这里就是GRUB引导程序。

2.2 Hello OS引导汇编代码

对应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
  1. 代码1~40行,用汇编定义的GRUB的多引导协议头,之所以有两个引导头,是为了兼容GRUB1和GRUB2。
  2. 代码44~52行,关掉中断,设定CPU的工作模式。
  3. 代码54~73行,初始化CPU的寄存器和C语言的运行环境。
  4. 代码78~87行,从GDT_START开始是CPU工作模式所需要的数据。

2.3 主函数

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

其中的printf不是应用程序库中的那个printf,而是需要我们自己实现。

2.4 控制计算机屏幕

我们要在屏幕上显示字符,就要编程操作显卡。

显卡把屏幕分成24行,每行80个字符,把这(24*80)个位置映射到以0xb8000地址开始的内存中,每两个字节对应一个字符,其中一个字节是字符的ASCII码,另一个字节为字符的颜色值。

img

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;
}

_strwrite函数正是将字符串里每个字符依次定入到0xb8000地址开始的显存中,而p_strdst每次加2,这也是为了跳过字符的颜色信息的空间。

2.5 编译过程

img

本文为互联网自动采集或经作者授权后发布,本文观点不代表立场,若侵权下架请联系我们删帖处理!文章出自:https://wangjiawei.blog.csdn.net/article/details/118429324
-- 展开阅读全文 --
安全面试之XSS(跨站脚本攻击)
« 上一篇 07-24

发表评论

成为第一个评论的人

热门文章

标签TAG

最近回复