当前位置: 首页 > 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实现…...

19c补丁后oracle属主变化,导致不能识别磁盘组

补丁后服务器重启&#xff0c;数据库再次无法启动 ORA01017: invalid username/password; logon denied Oracle 19c 在打上 19.23 或以上补丁版本后&#xff0c;存在与用户组权限相关的问题。具体表现为&#xff0c;Oracle 实例的运行用户&#xff08;oracle&#xff09;和集…...

【kafka】Golang实现分布式Masscan任务调度系统

要求&#xff1a; 输出两个程序&#xff0c;一个命令行程序&#xff08;命令行参数用flag&#xff09;和一个服务端程序。 命令行程序支持通过命令行参数配置下发IP或IP段、端口、扫描带宽&#xff0c;然后将消息推送到kafka里面。 服务端程序&#xff1a; 从kafka消费者接收…...

Debian系统简介

目录 Debian系统介绍 Debian版本介绍 Debian软件源介绍 软件包管理工具dpkg dpkg核心指令详解 安装软件包 卸载软件包 查询软件包状态 验证软件包完整性 手动处理依赖关系 dpkg vs apt Debian系统介绍 Debian 和 Ubuntu 都是基于 Debian内核 的 Linux 发行版&#xff…...

镜像里切换为普通用户

如果你登录远程虚拟机默认就是 root 用户&#xff0c;但你不希望用 root 权限运行 ns-3&#xff08;这是对的&#xff0c;ns3 工具会拒绝 root&#xff09;&#xff0c;你可以按以下方法创建一个 非 root 用户账号 并切换到它运行 ns-3。 一次性解决方案&#xff1a;创建非 roo…...

【Web 进阶篇】优雅的接口设计:统一响应、全局异常处理与参数校验

系列回顾&#xff1a; 在上一篇中&#xff0c;我们成功地为应用集成了数据库&#xff0c;并使用 Spring Data JPA 实现了基本的 CRUD API。我们的应用现在能“记忆”数据了&#xff01;但是&#xff0c;如果你仔细审视那些 API&#xff0c;会发现它们还很“粗糙”&#xff1a;有…...

UR 协作机器人「三剑客」:精密轻量担当(UR7e)、全能协作主力(UR12e)、重型任务专家(UR15)

UR协作机器人正以其卓越性能在现代制造业自动化中扮演重要角色。UR7e、UR12e和UR15通过创新技术和精准设计满足了不同行业的多样化需求。其中&#xff0c;UR15以其速度、精度及人工智能准备能力成为自动化领域的重要突破。UR7e和UR12e则在负载规格和市场定位上不断优化&#xf…...

Python 包管理器 uv 介绍

Python 包管理器 uv 全面介绍 uv 是由 Astral&#xff08;热门工具 Ruff 的开发者&#xff09;推出的下一代高性能 Python 包管理器和构建工具&#xff0c;用 Rust 编写。它旨在解决传统工具&#xff08;如 pip、virtualenv、pip-tools&#xff09;的性能瓶颈&#xff0c;同时…...

C++.OpenGL (14/64)多光源(Multiple Lights)

多光源(Multiple Lights) 多光源渲染技术概览 #mermaid-svg-3L5e5gGn76TNh7Lq {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-3L5e5gGn76TNh7Lq .error-icon{fill:#552222;}#mermaid-svg-3L5e5gGn76TNh7Lq .erro…...

Java + Spring Boot + Mybatis 实现批量插入

在 Java 中使用 Spring Boot 和 MyBatis 实现批量插入可以通过以下步骤完成。这里提供两种常用方法&#xff1a;使用 MyBatis 的 <foreach> 标签和批处理模式&#xff08;ExecutorType.BATCH&#xff09;。 方法一&#xff1a;使用 XML 的 <foreach> 标签&#xff…...

Caliper 配置文件解析:fisco-bcos.json

config.yaml 文件 config.yaml 是 Caliper 的主配置文件,通常包含以下内容: test:name: fisco-bcos-test # 测试名称description: Performance test of FISCO-BCOS # 测试描述workers:type: local # 工作进程类型number: 5 # 工作进程数量monitor:type: - docker- pro…...