当前位置: 首页 > news >正文

实现一个最简单的内核

更好的阅读体验,请点击 YinKai 's Blog | 实现一个最简单的内核。

​ 这篇文章带大家实现一个最简单的操作系统内核—— Hello OS。

PC 机的引导流程

​ 我们这里将借助 Ubuntu Linux 操纵系统上的 GRUB 引导程序来引导我们的 Hello OS。

​ 首先我们得了解一下,Hello OS 的引导流程:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

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

Hello OS 引导汇编代码

​ 我们的 Hello OS 总有 6 个文件,下面一一讲解。

Hello OS 的主函数
main.c
#include "vgastr.h"
void main()
{printf("Hello OS! I am YinKai");return;
}
entry.asm
; 多引导协议头(GRUB)
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 _entryALIGN 8
; GRUB 所需的多引导协议头
mbt_hdr:dd MBT_HDR_MAGICdd MBT_HDR_FLAGSdd -(MBT_HDR_MAGIC+MBT_HDR_FLAGS)dd mbt_hdrdd _startdd 0dd 0dd _entryALIGN 8
; GRUB2 所需的多引导协议头
mbt2_hdr:DD MBT_HDR2_MAGICDD 0DD mbt2_hdr_end - mbt2_hdrDD -(MBT_HDR2_MAGIC + 0 + (mbt2_hdr_end - mbt2_hdr))DW 2, 0DD 24DD mbt2_hdrDD _startDD 0DD 0DW 3, 0DD 12DD _entryDD 0DW 0, 0DD 8mbt2_hdr_end:ALIGN 8
_entry:; 关中断cli; 关不可屏蔽中断in al, 0x70or al, 0x80out 0x70, al; 重新加载GDTlgdt [GDT_PTR]jmp dword 0x8 :_32bits_mode_32bits_mode:; 初始化C语言可能会用到的寄存器mov ax, 0x10mov ds, axmov ss, axmov es, axmov fs, axmov gs, axxor eax,eaxxor ebx,ebxxor ecx,ecxxor edx,edxxor edi,edixor esi,esixor ebp,ebpxor esp,esp; 初始化栈,C语言需要栈才能工作mov esp,0x9000; 调用C语言函数maincall main; 让CPU停止执行指令
halt_step:haltjmp halt_step; GDT 全局描述符表
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

​ 这是一个引导加载程序,它是计算机启动过程中的第一个软件,它的主要任务是在计算机启动时,通过 GRUB 或 GRUB2 多引导协议头,初始化系统环境,设置 GDT,然后调用 C 语言的 main 函数。

控制计算机屏幕

​ 首先我们得知道显卡的字符模式的工作细节。

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

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

​ 了解原理之后,我们来自己实现 printf 函数:

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 函数,然后把字符串中的每个字符依次写入 0xb8000 地址开始的显存中。p_strdst 每次加 2 ,是为了跳过表示颜色值的字符,直接指向下一个字符的 ASCII 值。

​ 为了编译器能够正确识别我们的函数,我们还需要另写一个文件,保证函数调用的正确性。

vgastr.h
void _strwrite(char* string);
void printf(char* fmt, ...);
链接
hello.lds
ENTRY(_start)
OUTPUT_ARCH(i386)
OUTPUT_FORMAT(elf32-i386)
SECTIONS
{. = 0x200000;__begin_start_text = .;.start.text : ALIGN(4) { *(.start.text) }__end_start_text = .;__begin_text = .;.text : ALIGN(4) { *(.text) }__end_text = .;__begin_data = .;.data : ALIGN(4) { *(.data) }__end_data = .;__begin_rodata = .;.rodata : ALIGN(4) { *(.rodata) *(.rodata.*) }__end_rodata = .;__begin_kstrtab = .;.kstrtab : ALIGN(4) { *(.kstrtab) }__end_kstrtab = .;__begin_bss = .;.bss : ALIGN(4) { *(.bss) }__end_bss = .;
}

​ 这段代码是一个链接脚本,用于告诉链接器如何将各个目标文件组合成最终的可执行文件。

编译

​ 我们这里使用 make 工具进行系统编译,将每个代码模块编译最后链接成可执行的二进制文件。

Makefile
MAKEFLAGS = -sR
MKDIR = mkdir
RMDIR = rmdir
CP = cp
CD = cd
DD = dd
RM = rmASM		= nasm
CC		= gcc
LD		= ld
OBJCOPY	= objcopyASMBFLAGS	= -f elf -w-orphan-labels
CFLAGS		= -c -Os -std=c99 -m32 -Wall -Wshadow -W -Wconversion -Wno-sign-conversion  -fno-stack-protector -fomit-frame-pointer -fno-builtin -fno-common  -ffreestanding  -Wno-unused-parameter -Wunused-variable
LDFLAGS		= -s -static -T hello.lds -n -Map HelloOS.map 
OJCYFLAGS	= -S -O binaryHELLOOS_OBJS :=
HELLOOS_OBJS += entry.o main.o vgastr.o
HELLOOS_ELF = HelloOS.elf
HELLOOS_BIN = HelloOS.bin.PHONY : build clean all link binall: clean build link binclean:$(RM) -f *.o *.bin *.elfbuild: $(HELLOOS_OBJS)link: $(HELLOOS_ELF)
$(HELLOOS_ELF): $(HELLOOS_OBJS)$(LD) $(LDFLAGS) -o $@ $(HELLOOS_OBJS)
bin: $(HELLOOS_BIN)
$(HELLOOS_BIN): $(HELLOOS_ELF)$(OBJCOPY) $(OJCYFLAGS) $< $@%.o : %.asm$(ASM) $(ASMBFLAGS) -o $@ $<
%.o : %.c$(CC) $(CFLAGS) -o $@ $<

安装 Hello OS

​ 不同的系统,可能操作不同,我这里用的是 ubuntu。

安装编译环境
  • 安装汇编编译器
sudo apt-get install nasm
  • 安装gcc(该命令会安装包括gcc在内的所有软件)
sudo apt install build-essential
修改启动项等待时间

​ 修改启动项等待时间,以供我们选择启动项文件

sudo vim /etc/default/grub,打开文件,修改为 10 s

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

​ 使用 sudo update-grub 更新我们的修改。

:::warning

​ 每次使用这个命令之后,我们追加的启动项(后面会说到)就会被清除,需要重新添加。

:::

构建 HelloOS.bin 文件

​ 在自己的家目录下创建一个 HelloOS 文件夹,放入我们依赖的 6 个文件,代码及文件命名见上。

使用 make 构建

​ 在 HelloOS 目录下,使用 make 命令,即可获得 HelloOS.bin 文件,并将该文件移动到 /boot/ 目录下。(如果原本就有要将其删除,再放入。)

追加 GRUB 启动项

​ 使用 df /boot 获取文件系统名,以及文件系统的挂载点,我的如下:

文件系统          1K-块    已用    可用 已用% 挂载点
/dev/sda5      19947120 9921616 8986912   53% /

​ 写 grub 的引导文件,将下面的启动项代码插入到 /boot/grub/grub.cfg 文件末尾

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

:::warning

① 这里的 hd0,msdos? 需要根据 (4)中的 /dev/sda? 对应起来;

② 如果挂载点是 / 就需要在文件中写 /boot/HelloOS.bin;如果挂载点是 /boot,则直接写 /HelloOS.bin 即可

③ 如果该文件不可修改,可以用 root 权限修改该文件为可写文件。

:::

​ 最后使用 reboot 命令,即可重启系统,看到我们的 Hello OS 选项:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

​ 选择后,即可看到我们在主函数 main.c 中写的字符串啦~

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

小结

Hello OS 启动的流程主要包括以下步骤:

  1. 计算机上电: 当计算机上电时,主板上的 BIOS 固件开始执行。
  2. BIOS 初始化: BIOS 负责检测和初始化计算机硬件,包括处理器、内存等。
  3. 加载引导扇区: BIOS 根据启动设备配置加载引导扇区,通常是硬盘上的 MBR。
  4. 引导加载程序执行: 引导加载程序(如 GRUB)被加载,负责加载操作系统内核。
  5. GRUB 加载 Hello OS: GRUB 通过配置文件加载 Hello OS 的二进制文件(HelloOS.bin)。
  6. Hello OS 入口点: Hello OS 的入口点在 entry.asm 中,负责初始化系统环境。
  7. 切换到 32 位保护模式: _start 调用 _32bits_mode 将处理器切换到 32 位保护模式。
  8. C 语言的 main 函数: main.c 包含操作系统的主要逻辑,调用了输出字符串的函数。
  9. 屏幕输出: vgastr.c 中的 _strwriteprintf 负责向屏幕输出字符串。
  10. 系统初始化完成: Hello OS 在初始化完成后,等待主要逻辑执行完毕。
  11. CPU 停止执行指令: 使用 halt 指令让 CPU 停止执行,操作系统启动过程结束。
  12. 系统运行或重新启动: 如果需要,可以继续执行其他操作系统功能或重新启动计算机。

相关文章:

实现一个最简单的内核

更好的阅读体验&#xff0c;请点击 YinKai s Blog | 实现一个最简单的内核。 ​ 这篇文章带大家实现一个最简单的操作系统内核—— Hello OS。 PC 机的引导流程 ​ 我们这里将借助 Ubuntu Linux 操纵系统上的 GRUB 引导程序来引导我们的 Hello OS。 ​ 首先我们得了解一下&a…...

2024华为OD机试真题指南宝典—持续更新(JAVAPythonC++JS)【彻底搞懂算法和数据结构—算法之翼】

PC端可直接搜索关键词 快捷键&#xff1a;CtrlF 年份关键字、题目关键字等等 注意看本文目录-快速了解本专栏 文章目录 &#x1f431;2024年华为OD机试真题&#xff08;马上更新&#xff09;&#x1f439;2023年华为OD机试真题&#xff08;更新中&#xff09;&#x1f436;新…...

【12.23】转行小白历险记-算法02

不会算法的小白不是好小白&#xff0c;可恶还有什么可以难倒我这个美女的&#xff0c;不做花瓶第二天&#xff01; 一、螺旋矩阵 59. 螺旋矩阵 II - 力扣&#xff08;LeetCode&#xff09; 1.核心思路&#xff1a;确定循环的路线&#xff0c;左闭右开循环&#xff0c;思路简…...

k8s部署nginx-ingress服务

k8s部署nginx-ingress服务 经过大佬的拷打&#xff0c;终于把这块的内容配置完成了。 首先去 nginx-ingress官网查看相关内容。 核心就是这个&#xff1a; kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.8.2/deploy/static/prov…...

SpringBoot Elasticsearch全文搜索

文章目录 概念全文搜索相关技术Elasticsearch概念近实时索引类型文档分片(Shard)和副本(Replica) 下载启用SpringBoot整合引入依赖创建文档类创建资源库测试文件初始化数据创建控制器 问题参考 概念 全文搜索&#xff08;检索&#xff09;&#xff0c;工作原理&#xff1a;计算…...

Python 常用模块re

Python 常用模块re 【一】正则表达式 【1】说明 正则表达式是一种强大的文本匹配和处理工具&#xff0c;主要用于字符串的模式匹配、搜索和替换。正则表达式测试网址&#xff1a;正则表达式在线测试 正则表达式手册&#xff1a;正则表达式手册 【2】字符组 字符转使用[]表…...

【华为OD题库-106】全排列-java

题目 给定一个只包含大写英文字母的字符串S&#xff0c;要求你给出对S重新排列的所有不相同的排列数。如:S为ABA&#xff0c;则不同的排列有ABA、AAB、BAA三种。 解答要求 时间限制:5000ms,内存限制:100MB 输入描述 输入一个长度不超过10的字符串S&#xff0c;确保都是大写的。…...

Three.js 详细解析(持续更新)

1、简介&#xff1b; Three.js依赖一些要素&#xff0c;第一是scene&#xff0c;第二是render&#xff0c;第三是carmea npm install --save three import * as THREE from "three"; import { GLTFLoader } from "three/examples/jsm/loaders/GLTFLoader.js&quo…...

Unity中Shader平移矩阵

文章目录 前言方式一&#xff1a;对顶点本地空间下的坐标进行相加平移1、在属性面板定义一个四维变量记录在 xyz 上平移多少。2、在常量缓冲区进行申明3、在顶点着色器中&#xff0c;在进行其他坐标转化之前&#xff0c;对模型顶点本地空间下的坐标进行转化4、我们来看看效果 方…...

python dash 的学习笔记1

dash 用python开发web界面 https://dash.plotly.com/ 官方上支持jula F# python一类。当然我只会python只学习python中使用dash. 要做一个APP&#xff0c;用php,java以及.net都可以写&#xff0c;只所有选择python是因为最近在用这一个。同时也发现python除了慢全是优点。 资料…...

SQLITE如何同时查询出第一条和最后一条两条记录

一个时间记录表&#xff0c;需要同时得到整个表或一段时间内第一条和最后一条两条记录&#xff0c;按如下方法会提示错误&#xff1a;ORDER BY clause should come after UNION not before select * from sdayXX order by op_date asc limit 1 union select * from sday…...

四、ensp配置ftp服务器实验

文章目录 实验内容实验拓扑操作步骤配置路由器为ftp server 实验内容 本实验模拟企业网络。PC-1为FTP 用户端设备&#xff0c;需要访问FTP Server&#xff0c;从服务器上下载或上传文件。出于安全角度考虑&#xff0c;为防止服务器被病毒文件感染&#xff0c;不允许用户端直接…...

VS2020使用MFC开发一个贪吃蛇游戏

背景&#xff1a; 贪吃蛇游戏 按照如下步骤实现:。初始化地图 。通过键盘控制蛇运动方向&#xff0c;注意重新设置运动方向操作。 。制造食物。 。让蛇移动&#xff0c;如果吃掉食物就重新生成一个食物&#xff0c;如果会死亡就break。用蛇的坐标将地图中的空格替换为 #和”将…...

【经典LeetCode算法题目专栏分类】【第9期】深度优先搜索DFS与并查集:括号生成、岛屿问题、扫雷游戏

《博主简介》 小伙伴们好&#xff0c;我是阿旭。专注于人工智能AI、python、计算机视觉相关分享研究。 ✌更多学习资源&#xff0c;可关注公-仲-hao:【阿旭算法与机器学习】&#xff0c;共同学习交流~ &#x1f44d;感谢小伙伴们点赞、关注&#xff01; 《------往期经典推荐--…...

字符设备驱动开发-注册-设备文件创建

一、字符设备驱动 linux系统中一切皆文件 1、应用层&#xff1a; APP1 APP2 ... fd open("led驱动的文件"&#xff0c;O_RDWR); read(fd); write(); close(); 2、内核层&#xff1a; 对灯写一个驱动 led_driver.c driver_open(); driver_read(); driver_write(…...

TrustZone之可信操作系统

有许多可信内核&#xff0c;包括商业和开源的。一个例子是OP-TEE&#xff0c;最初由ST-Ericsson开发&#xff0c;但现在是由Linaro托管的开源项目。OP-TEE提供了一个功能齐全的可信执行环境&#xff0c;您可以在OP-TEE项目网站上找到详细的描述。 OP-TEE的结构如下图所示&…...

java定义三套场景接口方案

一、背景 在前后端分离开发的背景下&#xff0c;后端java开发人员现在只需要编写接口接口。特别是使用微服务开发的接口。resful风格接口。那么一般后端接口被调用有下面三种场景。一、不需要用户登录的接口调用&#xff0c;第二、后端管理系统接口调用&#xff08;需要账号密…...

idea连接数据库,idea连接MySQL,数据库驱动下载与安装

文章目录 普通Java工程先创建JAVA工程JDBC连接数据库测试连接 可视化连接数据库数据库驱动下载与安装常用的数据库驱动下载MySQL数据库Oracle数据库SQL Server 数据库PostgreSQL数据库 下载MySQL数据库驱动JDBC连接各种数据库的连接语句MySQL数据库Oracle数据库DB2数据库sybase…...

Redis-实践知识

转自极客时间Redis 亚风 原文视频&#xff1a;https://u.geekbang.org/lesson/535?article681062 Redis最佳实践 普通KEY Redis 的key虽然可以自定义&#xff0c;但是最好遵循下面几个实践的约定&#xff1a; 格式&#xff1a;[业务名称]:[数据名]:[id] 长度不超过44字节 不…...

多维时序 | MATLAB实现SSA-CNN-SVM麻雀算法优化卷积神经网络-支持向量机多变量时间序列预测

多维时序 | MATLAB实现SSA-CNN-SVM麻雀算法优化卷积神经网络-支持向量机多变量时间序列预测 目录 多维时序 | MATLAB实现SSA-CNN-SVM麻雀算法优化卷积神经网络-支持向量机多变量时间序列预测预测效果基本介绍模型描述程序设计参考资料 预测效果 基本介绍 多维时序 | MATLAB实现…...

(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)

题目&#xff1a;3442. 奇偶频次间的最大差值 I 思路 &#xff1a;哈希&#xff0c;时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况&#xff0c;哈希表这里用数组即可实现。 C版本&#xff1a; class Solution { public:int maxDifference(string s) {int a[26]…...

零门槛NAS搭建:WinNAS如何让普通电脑秒变私有云?

一、核心优势&#xff1a;专为Windows用户设计的极简NAS WinNAS由深圳耘想存储科技开发&#xff0c;是一款收费低廉但功能全面的Windows NAS工具&#xff0c;主打“无学习成本部署” 。与其他NAS软件相比&#xff0c;其优势在于&#xff1a; 无需硬件改造&#xff1a;将任意W…...

超短脉冲激光自聚焦效应

前言与目录 强激光引起自聚焦效应机理 超短脉冲激光在脆性材料内部加工时引起的自聚焦效应&#xff0c;这是一种非线性光学现象&#xff0c;主要涉及光学克尔效应和材料的非线性光学特性。 自聚焦效应可以产生局部的强光场&#xff0c;对材料产生非线性响应&#xff0c;可能…...

使用分级同态加密防御梯度泄漏

抽象 联邦学习 &#xff08;FL&#xff09; 支持跨分布式客户端进行协作模型训练&#xff0c;而无需共享原始数据&#xff0c;这使其成为在互联和自动驾驶汽车 &#xff08;CAV&#xff09; 等领域保护隐私的机器学习的一种很有前途的方法。然而&#xff0c;最近的研究表明&…...

《通信之道——从微积分到 5G》读书总结

第1章 绪 论 1.1 这是一本什么样的书 通信技术&#xff0c;说到底就是数学。 那些最基础、最本质的部分。 1.2 什么是通信 通信 发送方 接收方 承载信息的信号 解调出其中承载的信息 信息在发送方那里被加工成信号&#xff08;调制&#xff09; 把信息从信号中抽取出来&am…...

TRS收益互换:跨境资本流动的金融创新工具与系统化解决方案

一、TRS收益互换的本质与业务逻辑 &#xff08;一&#xff09;概念解析 TRS&#xff08;Total Return Swap&#xff09;收益互换是一种金融衍生工具&#xff0c;指交易双方约定在未来一定期限内&#xff0c;基于特定资产或指数的表现进行现金流交换的协议。其核心特征包括&am…...

【JavaWeb】Docker项目部署

引言 之前学习了Linux操作系统的常见命令&#xff0c;在Linux上安装软件&#xff0c;以及如何在Linux上部署一个单体项目&#xff0c;大多数同学都会有相同的感受&#xff0c;那就是麻烦。 核心体现在三点&#xff1a; 命令太多了&#xff0c;记不住 软件安装包名字复杂&…...

AspectJ 在 Android 中的完整使用指南

一、环境配置&#xff08;Gradle 7.0 适配&#xff09; 1. 项目级 build.gradle // 注意&#xff1a;沪江插件已停更&#xff0c;推荐官方兼容方案 buildscript {dependencies {classpath org.aspectj:aspectjtools:1.9.9.1 // AspectJ 工具} } 2. 模块级 build.gradle plu…...

html-<abbr> 缩写或首字母缩略词

定义与作用 <abbr> 标签用于表示缩写或首字母缩略词&#xff0c;它可以帮助用户更好地理解缩写的含义&#xff0c;尤其是对于那些不熟悉该缩写的用户。 title 属性的内容提供了缩写的详细说明。当用户将鼠标悬停在缩写上时&#xff0c;会显示一个提示框。 示例&#x…...

GO协程(Goroutine)问题总结

在使用Go语言来编写代码时&#xff0c;遇到的一些问题总结一下 [参考文档]&#xff1a;https://www.topgoer.com/%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B/goroutine.html 1. main()函数默认的Goroutine 场景再现&#xff1a; 今天在看到这个教程的时候&#xff0c;在自己的电…...