首页 > 其他分享 >手写操作系统:一切从主引导扇区开始

手写操作系统:一切从主引导扇区开始

时间:2024-07-23 19:24:45浏览次数:11  
标签:引导 boot 扇区 mov 从主 ax 手写 加载

项目简介

本项目将从零实现一个基于 x86 32位的操作系统,系统支持多进程的运行,虚拟内存,进程与进程之间隔离,文件系统。

开发语言:汇编语言与C语言,主要是C语言。

开发工具: gcc编译器,gdb调试器,qemu虚拟机,dd工具 ,CMake .........

开发环境:windows11操作系统,VScode编辑器。

计算机启动流程

要开发一个操作系统,首先我们要知道操作系统的加载过程。

当系统上电后,CPU首先会初始化然后执行的BIOS的代码。BIOS会检查系统硬件,确保一切正常。这一阶段也称为 POST,即开机自检

完成自检后BIOS会加载硬盘上的第一个物理扇区(扇区0)到内存的 0x7c00 处,然后CPU会跳转到 0x7c00 处执行。

我们一般称第一个物理扇区为主引导扇区,主引导扇区上的程序称为主引导程序。主引导程序负责从硬盘加载操作系统,然后跳转至操作系统执行。因为一个扇区是512字节,所以主引导程序的大小必须在512字节以内。同时主引导扇区的最后两个字节必须 0x55与0xAA,用于告诉BIOS这是一个有效的主引导扇区。

主引导程序 

因为主引导扇区的程序只能有512字节,所以我们无法在主引导扇区完成太多工作,我们这里采用二级引导模式。由主引导扇区加载二级引导程序,然后在二级引导程序中完成加载内核前的准备工作并加载内核

在主引导程序中,我们首先完成段寄存器的初始化。我们需要将段寄存器全部初始化为 0 ,以平坦模式访问内存。

这里简单介绍一下在x86实模式下的内存访问模式,在实模式下CPU有 20 根地址线能够标识 2^20 个地址即 1MB,而CPU中的寄存器只有 16 位,一个寄存器只能存储 0x0000~0xFFFF 内的数,那么如何访问20位的地址?

实模式下的内存访问依赖于段寄存器和偏移量。地址由一个16位的段寄存器值和一个16位的偏移量组成。实际的物理地址是通过将段寄存器的值左移4位(乘以16)然后加上偏移量得到的。这意味着每个段的最大大小为64KB。  

在这里我们将段寄存器的值都设为 0 。只访问 64 KB内的内存,在二级引导程序设置了GDT表后开启A20地址线后访问全部的 4GB 内存。

  	.code16
 	.text
	.global _start
	.extern boot_entry
_start:
	//初始化寄存器
	mov $0 , %ax
    mov %ax, %ds
    mov %ax, %ss
    mov %ax, %es
    mov %ax, %fs
    mov %ax, %gs

	mov $_start , %sp

这里我们使用 .code16 伪指令生成 16 位模式下的机器码。

.code16指示汇编器当前代码段应使用16位的指令集和寄存器,这里我们处在实模式下即16位环境,在之后进入保护模式后就不用再指定,汇编器默认生成32位机器码。

然后我们初始化 ds ss es fs gs 段寄存器。同时将_start的值为栈指针寄存器sp的值。接下来我们使用中断加载磁盘中的主引导程序。

read:
	//加载二级引导程序进入内存
	//使用BIOS中断
	//AH=02H,AL=扇区数,CH=柱面,CL=扇区
	//DH=磁头,DL=驱动器,ES:BX 加载到的内存地址
    mov $0x8000, %bx
	mov $0x2, %ah
	mov $0x2, %cx
	mov $64,  %al
	mov $0x80, %dx
    int $0x13
    //CF=0 操作成功
	jc read

这里我们使用 13 号中断,从硬盘的第二个扇区开始读取,读取64个扇区,数据加载到0x8000处。

寄存器使用:

  • AH:操作代码,定义要执行的操作类型。

  • AL:其他参数,如扇区号、驱动器号等,取决于AH的值。

  • CH:柱面号的高8位。

  • CL:柱面号的低6位和扇区号的高2位。

  • DH:磁头号。

  • DL:驱动器号和设备类型。

  • ES:BX:指向内存缓冲区的指针,用于存储读取的数据。

常见的功能代码:

  • 0x02:读取磁盘扇区。

  • 0x03:写入磁盘扇区。

下面我们跳转至C语言环境中,通过函数调用跳转至二级引导程序。

	//进入C语言环境
	jmp boot_entry

我们在程序的开头声明了外部符号 boot_entry ,在编译时链接器会将这个寻找这个函数,并将jmp后的符号替换为boot_entry的内存地址。这样就实现了汇编语言跳转至C语言函数。

在另一个文件中我们定义了函数 boot_entry 。

__asm__(".code16gcc");

#include "boot.h"        //boot.h为空
#define LOADER_START 0x8000

//Boot的C入口函数
 
void boot_entry() 
{
    ((void (*)(void))LOADER_START)();  //跳转至loader执行
} 

我们知道:函数指针直接指向函数的代码在内存中的起始地址。当调用这个指针时,程序会跳转到这个地址执行函数的代码。

所以我们也可以用内存地址调用函数,在上面我们通过将 0x8000 强转为函数指针然后通过调用这个函数指针使程序跳转至二级引导程序运行。

现在我们已经基本完成主引导程序的工作。最后我们需要设置主引导扇区的标志位 0x55 与 0xAA。

	//引导结束段
	.section boot_end, "ax"

boot_sig: .byte 0x55, 0xaa

.section boot_end, "ax"这行代码指示汇编器创建一个名为 boot_end 的段。这个段的属性是 "ax",意味着它既可以被访问(a),也可以被执行(x)。在这个段后我们使用 .byte 伪指令写入了 0x55 与 0xAA。那么如何使得这个段在扇区的最后两个字节呢。

在CMake中,我们设置了链接器选项。

set(CMAKE_EXE_LINKER_FLAGS "-m elf_i386  -Ttext=0x7c00  --section-start boot_end=0x7dfe")
  •  -m elf_i386:这个选项告诉链接器生成一个适用于i386架构的ELF格式的可执行文件。这是32位x86架构的标准格式。
  • -Ttext=0x7c00:这个选项指定了文本段(.text section)的起始地址为0x7c00。这是创建传统启动扇区的关键,因为BIOS会在启动时加载启动设备的首个扇区(512字节)到内存地址0x7c00处,并从这个地址开始执行。
  • --section-start boot_end=0x7dfe:这个选项为boot_end段指定了一个起始地址0x7dfe。这通常用于将引导扇区的结束标志(通常是两个字节的0x55AA)放置在引导扇区的最后两个字节位置。boot_end段紧接在引导程序代码之后,确保引导扇区的总大小不超过512字节。

由此我们完成了主引导扇区程序的编写。

代码汇总 

//start.S
//主引导程序,启动时由硬件加载运行,然后完成对二级引导程序loader的加载
//该部分程序存储于磁盘的第1个扇区,在计算机启动时将会由BIOS加载到0x7c00处
//之后,将由BIOS跳转至0x7c00处开始运行

//16位代码
  	.code16
 	.text
	.global _start
	.extern boot_entry
_start:
	//初始化寄存器
	mov $0 , %ax
    mov %ax, %ds
    mov %ax, %ss
    mov %ax, %es
    mov %ax, %fs
    mov %ax, %gs

	mov $_start , %sp
read:
	//加载二级引导程序进入内存
	//使用BIOS中断
	//AH=02H,AL=扇区数,CH=柱面,CL=扇区
	//DH=磁头,DL=驱动器,ES:BX 加载到的内存地址
    mov $0x8000, %bx
	mov $0x2, %ah
	mov $0x2, %cx
	mov $64,  %al
	mov $0x80, %dx
    int $0x13
    //CF=0 操作成功
	jc read
    
	//进入C语言环境
	jmp boot_entry
		
	//引导结束段
	.section boot_end, "ax"

boot_sig: .byte 0x55, 0xaa
//boot.c
__asm__(".code16gcc");

#include "boot.h"
#define LOADER_START 0x8000

//Boot的C入口函数
 
void boot_entry() 
{
    ((void (*)(void))LOADER_START)();  //跳转至loader执行
} 

标签:引导,boot,扇区,mov,从主,ax,手写,加载
From: https://blog.csdn.net/2301_80926085/article/details/140523233

相关文章

  • 手写Kd树(C++模板非递归实现)
    手写Kd树(C++模板非递归实现)1.Kd树1.1Kd树简介1.2Kd树的建立1.3Kd树的查找2.C++完整代码实现3.测试代码3.1代码实现3.2测试结果4.与PCL中的Kd树做对比本文实现的Kd树实现参考了高翔博士的书《自动驾驶与机器人中的slam技术从理论到实践》;高博士原书中是递归......
  • 卷积神经网络CNN实战:MINST手写数字识别——数据集下载与网络训练
    数据集下载这一部分比较简单,就不过多赘述了,把代码粘贴到自己的项目文件里,运行一下就可以下载了。fromtorchvisionimportdatasets,transforms#定义数据转换,将数据转换为张量并进行标准化transform=transforms.Compose([transforms.ToTensor(),#转换为张量......
  • 用 300 行代码手写提炼 Spring 核心原理 [2]
    系列文章用300行代码手写提炼Spring核心原理[1]用300行代码手写提炼Spring核心原理[2]上文中我们实现了mini-spring的1.0版本,接下来我们在此基础上进行优化,将init()方法中的代码进行封装。按照之前的思路,先搭建基础框架,再“填肉注血”。初始化阶段in......
  • 用 300 行代码手写提炼 Spring 核心原理 [1]
    手写一个mini版本的Spring框架是一个很好的实践项目,可以让你对框架的核心概念和实现有更深刻的理解。接下来我们从0-1逐层深入,一步一步揭开Spring的神秘面纱。自定义配置配置application.properties为了解析方便,我们用application.properties来代替application.......
  • 用 300 行代码手写提炼 Spring 核心原理 [1]
    手写一个mini版本的Spring框架是一个很好的实践项目,可以让你对框架的核心概念和实现有更深刻的理解。接下来我们从0-1逐层深入,一步一步揭开Spring的神秘面纱。自定义配置配置application.properties为了解析方便,我们用application.properties来代替application.......
  • 【深度学习入门项目】多层感知器(MLP)实现手写数字识别
    多层感知器(MLP)实现手写数字识别导入必要的包获得软件包的版本信息下载并可视化数据查看一个batch的数据查看图片细节信息设置随机种子定义模型架构Buildmodel_1Buildmodel_2TraintheNetwork(30marks)Trainmodel_1Trainmodel_1Visualizethetrainingprocess......
  • JavaScript - jSignature移动端手写签名
    <html><head><scriptsrc="https://cdn.bootcdn.net/ajax/libs/jquery/3.7.1/jquery.min.js"></script><scriptsrc="https://cdn.bootcdn.net/ajax/libs/jSignature/2.1.3/jSignature.min.js"></script>......
  • 基于mnist数据集的手写数字识别模型的训练可视化预测
    使用 tensorflow库创建训练模型数据集使用公开的mnist 一、构建模型fromtensorflow.keras.layersimportDense,DropoutimporttensorflowastfdefmnistModel():model=tf.keras.Sequential([tf.keras.layers.Flatten(input_shape=(28,28)),#对......
  • MP+XML手写sql语句分页查询
    原则:让IPage接收从数据库查处的记录@AutowaireprivateUserMapperuserMapper;publicPageDTO<UserVO>pageUser(UserPageQueryquery){IPage<UserVO>page=newPage<>(query.getPageNo(),2);page=userMapper.PageAndXml(query,page);List<U......
  • 手写数字识别——KNN模型实现
    MNIST手写数字识别        MNIST手写数字数据库有一个包含60,000个示例的训练集和一个包含10,000个示例的测试集。每个图像高28像素,宽28像素,共784个像素。每个像素取值范围[0,255],取值越大意味着该像素颜色越深    下载:http://yann.lecun.com/e......