手写简单操作系统内核

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

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





相关文章
|
2月前
|
存储 调度
探索操作系统的心脏:内核与用户空间的交互
在数字世界的每一次呼吸中,操作系统扮演着至关重要的角色。本文将深入探讨操作系统的核心组件——内核与用户空间之间的神秘舞蹈。通过直观的比喻和生动的代码片段,我们将一窥这场幕后的交响曲,了解它们是如何协同工作以支持我们的计算需求的。从简单的文件读写到复杂的网络通信,每一个操作背后都隐藏着内核与用户空间之间精妙的互动。准备好跟随我们的脚步,一起揭开操作系统的神秘面纱。
33 3
|
1月前
|
安全 Linux 开发者
探索操作系统的心脏:内核与用户空间的交互
在数字世界的每一次点击和命令背后,隐藏着一个复杂而精妙的操作系统世界。本文将带你走进这个世界的核心,揭示内核与用户空间的神秘交互。通过深入浅出的解释和直观的代码示例,我们将一起理解操作系统如何协调硬件资源,管理进程和内存,以及提供文件系统服务。无论你是编程新手还是资深开发者,这篇文章都将为你打开一扇通往操作系统深层原理的大门。让我们一起开始这段旅程,探索那些支撑我们日常数字生活的技术基石吧!
45 6
|
1月前
|
存储 缓存 网络协议
Linux操作系统的内核优化与性能调优####
本文深入探讨了Linux操作系统内核的优化策略与性能调优方法,旨在为系统管理员和高级用户提供一套实用的指南。通过分析内核参数调整、文件系统选择、内存管理及网络配置等关键方面,本文揭示了如何有效提升Linux系统的稳定性和运行效率。不同于常规摘要仅概述内容的做法,本摘要直接指出文章的核心价值——提供具体可行的优化措施,助力读者实现系统性能的飞跃。 ####
|
1月前
|
缓存 监控 网络协议
Linux操作系统的内核优化与实践####
本文旨在探讨Linux操作系统内核的优化策略与实际应用案例,深入分析内核参数调优、编译选项配置及实时性能监控的方法。通过具体实例讲解如何根据不同应用场景调整内核设置,以提升系统性能和稳定性,为系统管理员和技术爱好者提供实用的优化指南。 ####
|
2月前
|
存储 Linux 开发者
探索操作系统的内核——从理论到实践
操作系统是计算机科学的核心,它像一位默默无闻的指挥官,协调着硬件和软件之间的复杂关系。本文将深入操作系统的心脏——内核,通过直观的解释和丰富的代码示例,揭示其神秘面纱。我们将一起学习进程管理、内存分配、文件系统等关键概念,并通过实际代码,体验内核编程的魅力。无论你是初学者还是有经验的开发者,这篇文章都将带给你新的视角和知识。
|
1月前
|
机器学习/深度学习 人工智能 物联网
操作系统的心脏——深入理解内核机制
在本文中,我们揭开操作系统内核的神秘面纱,探索其作为计算机系统核心的重要性。通过详细分析内核的基本功能、类型以及它如何管理硬件资源和软件进程,我们将了解内核是如何成为现代计算不可或缺的基础。此外,我们还会探讨内核设计的挑战和未来趋势,为读者提供一个全面的内核知识框架。
|
1月前
|
消息中间件 安全 Linux
深入探索Linux操作系统的内核机制
本文旨在为读者提供一个关于Linux操作系统内核机制的全面解析。通过探讨Linux内核的设计哲学、核心组件、以及其如何高效地管理硬件资源和系统操作,本文揭示了Linux之所以成为众多开发者和组织首选操作系统的原因。不同于常规摘要,此处我们不涉及具体代码或技术细节,而是从宏观的角度审视Linux内核的架构和功能,为对Linux感兴趣的读者提供一个高层次的理解框架。
|
2月前
|
存储 调度 开发者
探索操作系统的心脏:内核与用户空间的交互之旅
在数字世界的无限广阔中,操作系统扮演着枢纽的角色,连接硬件与软件,支撑起整个计算生态。本篇文章将带领读者深入操作系统的核心——内核,揭示其与用户空间的神秘交互。我们将透过生动的例子和易于理解的比喻,深入浅出地探讨这一复杂主题,旨在为非专业读者揭开操作系统的神秘面纱,同时为有一定基础的读者提供更深层次的认识。从进程管理到内存分配,从文件系统到设备驱动,每一个环节都是精确而优雅的舞蹈,它们共同编织出稳定而高效的计算体验。让我们开始这场奇妙之旅,一探操作系统背后的科学与艺术。
33 5
|
2月前
|
缓存 并行计算 Linux
深入解析Linux操作系统的内核优化策略
本文旨在探讨Linux操作系统内核的优化策略,包括内核参数调整、内存管理、CPU调度以及文件系统性能提升等方面。通过对这些关键领域的分析,我们可以理解如何有效地提高Linux系统的性能和稳定性,从而为用户提供更加流畅和高效的计算体验。
37 2
|
2月前
|
缓存 网络协议 Linux
深入探索Linux操作系统的内核优化策略####
本文旨在探讨Linux操作系统内核的优化方法,通过分析当前主流的几种内核优化技术,结合具体案例,阐述如何有效提升系统性能与稳定性。文章首先概述了Linux内核的基本结构,随后详细解析了内核优化的必要性及常用手段,包括编译优化、内核参数调整、内存管理优化等,最后通过实例展示了这些优化技巧在实际场景中的应用效果,为读者提供了一套实用的Linux内核优化指南。 ####
53 1