手写简单操作系统内核

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

1 操作过程


我的环境:

 在virtualBox上面先安装好虚拟机Ubuntu 16.04



源码:

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



1.1 设置grub进入引导菜单


安装nasm,才能执行make

先安装nasm:


sudo apt-get install -y nasm


然后在HelloOS目录下执行:

make -f Makefile


就可以得到HelloOS.bin文件了


0.png


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


先修改文件:


/etc/default/grub


1.png


如果修改的时候报错“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
}


2.png


3.png


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


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


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


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


4.png


5.png

2 原理


2.1 Hello OS的引导流程



6.png

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~40行,用汇编定义的GRUB的多引导协议头,之所以有两个引导头,是为了兼容GRUB1和GRUB2。


代码44~52行,关掉中断,设定CPU的工作模式。


代码54~73行,初始化CPU的寄存器和C语言的运行环境。


代码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码,另一个字节为字符的颜色值。


7.png


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 编译过程


微信图片_20220422224627.png





相关文章
|
24天前
|
存储 物联网 调度
操作系统的心脏:内核深度解析
在数字世界的构建中,操作系统扮演着基石的角色,而其核心—内核,则是这一复杂系统的灵魂。本文将深入探讨操作系统内核的工作原理,揭示它是如何管理硬件资源、运行程序以及提供系统服务的。通过理解内核的结构和功能,我们可以更好地把握计算机系统的运作机制,进而优化和创新我们的技术实践。
|
2月前
|
安全 Linux 编译器
探索Linux内核的奥秘:从零构建操作系统####
本文旨在通过深入浅出的方式,带领读者踏上一段从零开始构建简化版Linux操作系统的旅程。我们将避开复杂的技术细节,以通俗易懂的语言,逐步揭开Linux内核的神秘面纱,探讨其工作原理、核心组件及如何通过实践加深理解。这既是一次对操作系统原理的深刻洞察,也是一场激发创新思维与实践能力的冒险。 ####
|
4天前
|
存储 调度 开发者
探索操作系统的心脏:内核与用户空间的交互之旅
在数字世界的无限广阔中,操作系统扮演着枢纽的角色,连接硬件与软件,支撑起整个计算生态。本篇文章将带领读者深入操作系统的核心——内核,揭示其与用户空间的神秘交互。我们将透过生动的例子和易于理解的比喻,深入浅出地探讨这一复杂主题,旨在为非专业读者揭开操作系统的神秘面纱,同时为有一定基础的读者提供更深层次的认识。从进程管理到内存分配,从文件系统到设备驱动,每一个环节都是精确而优雅的舞蹈,它们共同编织出稳定而高效的计算体验。让我们开始这场奇妙之旅,一探操作系统背后的科学与艺术。
17 5
|
7天前
|
安全 数据处理 调度
探索操作系统的心脏:内核与用户空间的交互之旅
操作系统,这个现代计算机体系的守门人,承载着软件与硬件间复杂而精妙的对话。本文将深入其核心,揭秘内核与用户空间之间如何协同工作,确保数据安全且高效地流动。我们将透过代码示例的镜头,观察这一过程的具体实现,同时反思在设计与使用操作系统时面临的挑战与机遇。
|
9天前
|
存储 算法 调度
探索操作系统的心脏:内核设计与实现
在数字世界的庞大机器中,操作系统扮演着至关重要的角色。本文将深入浅出地探讨操作系统的核心——内核的设计原理与实现细节。我们将从内核的概念出发,逐步深入到内核的各个组成部分,包括进程管理、内存管理、文件系统以及输入输出系统的工作机制。通过本文,读者不仅能够了解操作系统内核的基本框架,还将掌握如何通过编程实践加深对操作系统核心概念的理解。让我们一起揭开操作系统内核的神秘面纱,探索它的精妙设计,并体会编程实践中的乐趣和挑战。
29 2
|
15天前
|
缓存 资源调度 安全
深入探索Linux操作系统的心脏——内核配置与优化####
本文作为一篇技术性深度解析文章,旨在引领读者踏上一场揭秘Linux内核配置与优化的奇妙之旅。不同于传统的摘要概述,本文将以实战为导向,直接跳入核心内容,探讨如何通过精细调整内核参数来提升系统性能、增强安全性及实现资源高效利用。从基础概念到高级技巧,逐步揭示那些隐藏在命令行背后的强大功能,为系统管理员和高级用户打开一扇通往极致性能与定制化体验的大门。 --- ###
44 9
|
18天前
|
存储 人工智能 安全
操作系统的心脏——内核深度解析
【10月更文挑战第29天】 本文深入探讨了操作系统的核心组件——内核,包括其定义、功能、架构以及在现代计算中的重要性。通过对比不同操作系统内核的设计哲学和技术实现,揭示了内核如何影响系统性能、稳定性和安全性。此外,文章还讨论了未来内核技术的潜在发展方向,为读者提供了一个全面了解内核工作原理的平台。
|
15天前
|
存储 消息中间件 算法
深入探索操作系统的心脏——内核机制解析
本文旨在揭示操作系统核心——内核的工作原理,通过剖析其关键组件与机制,为读者提供一个清晰的内核结构图景。不同于常规摘要的概述性内容,本文摘要将直接聚焦于内核的核心概念、主要功能以及其在系统管理中扮演的角色,旨在激发读者对操作系统深层次运作原理的兴趣与理解。
|
15天前
|
缓存 运维 网络协议
深入Linux内核架构:操作系统的核心奥秘
深入Linux内核架构:操作系统的核心奥秘
33 2
|
16天前
|
算法 调度 C语言
探索操作系统的心脏:内核与用户空间的交互
【10月更文挑战第36天】本文将深入探讨操作系统的核心组件—内核,以及它如何与用户空间进行交互。我们将通过浅显易懂的语言和生动的例子来揭示这一复杂主题的面纱。文章不仅涉及理论知识,还会展示具体的代码示例,帮助读者更好地理解内核机制。无论你是初学者还是有一定基础的开发者,这篇文章都将为你提供新的视角和深入的理解。
下一篇
无影云桌面