OS架构整理

OS架构整理

  • 引导启动部分
    • bios bootloader区别
    • 启动流程(x86 BIOS 启动):
    • bios
    • boot_loader
      • 3.切换进保护模式
        • 实模式的限制
        • 如何切换进保护模式
      • 加载kernel到内存地址1M
      • 加载内核映像文件
      • elf
  • 一些基础知识
    • 链接脚本与代码数据段
      • 创建GDT表
        • 段页式内存管理
    • 显示字符串
    • bios中断向量表中来显示
    • 内联汇编来显示

引导启动部分

bios bootloader区别

[BIOS] (硬件开机,里面有写好的固件,上电自检)↓ 读取硬盘第0扇区 → 加载到内存地址 0x7C00
[boot] (512字节以内的 MBR/bootloader阶段1)(因为只有512字节,太小了所以用了二级引导)↓ 加载并跳转到更复杂的 loader(通常是 2阶)
[loader] (多段程序,支持文件系统、内核加载等)↓ 加载操作系统内核(kernel)
[OS内核] (操作系统正式启动)

在这里插入图片描述
前三步是我们自己控制不了的
我的代码是从磁盘加载
在这里插入图片描述

启动流程(x86 BIOS 启动):

在这里插入图片描述
这个第0扇区的代码需要自己去写
首先Bios上电自检,然后将磁盘的第一个扇区512字节放入到内存地址0x7c00,检查是否以0x55 0xaa结束,从而判断这是否是有效的MBR
,如果是的话就进行引导

bios

  1. 加电启动(Power On)
  2. BIOS 执行 POST(Power-On Self-Test)
  3. 搜索可启动设备(硬盘、U盘、光盘…)
  4. 找到启动设备后:
    • 读取该设备的 第 1 个扇区(LBA 0,大小 = 512 字节)
    • 把它加载到内存地址 0x0000:0x7C00(实地址 = 0x7C00
  5. 检查最后两个字节是否为 0x55AA(有效的引导扇区签名)
  6. 如果正确 → 跳转到 0x7C00 执行

在 BIOS 启动模式中,BIOS 会将硬盘的 第 0 扇区(MBR)加载到 0x7C00,并从那里开始执行。

读取磁盘又两种方式
一种是int13(bios)
一种是LAB(复杂一点,在loader中实现)

x86在上电后自动进入实模式,1m内存 无分页机制 寄存器也只能用16位
但是1m内存的话要访问完需要20位地址,我们就是将段基址<<4+偏移构成20位
段基址的值是存在CS/DS/SS/ES/FS/GS(段寄存器)中
这个实模式下1M大小的内存映射情况:
在这里插入图片描述
我的boot是在start.s中写的
boot的初始化: 主要就是将段寄存器先赋初值0,简化代码,栈顶指针赋值0x7c00,表示我的boot在0x7c00地址以下的栈区,大概30kb左右是满足这个大小的
boot跳转到loader二级引导
在这里插入图片描述
在这里插入图片描述

读取多个磁盘加载到内存地址上:用了bios中断向量表 0x13 从第一个扇区开始 分配64个扇区(大概32kb) 如果读取磁盘正确后进入C环境并跳转到loader(jmp或者call)
在这里插入图片描述
.extern C函数名字(boot的一个跳转函数)

boot_loader

在这里插入图片描述

cmake的链接器表示我loader 加载到0x8000的地址,start.s放在cmake加入的工程文件的最开头,这样就可以保证加载到0x8000时在start.s
在这里插入图片描述

因为512字节显然比较小,没办法完成这么多功能,所以我做了一个二级引导
1.内联汇编显示字符串
2.检测内存容量 0x15(boot_info)
在这里插入图片描述
检测10块可用内存区域

3.切换进保护模式

实模式的限制

1.只能访问1MB内存,内核寄存器最大为16位宽
2.所有的操作数最大为16位宽
3.没有任何保护机制
4.没有特权级支持
5.没有分页机制和虚拟内存的支持

如何切换进保护模式

在这里插入图片描述

首先保证过程原子性,禁用中断,然后打开A20地址线让其访问1m以上的内存地址,然后初始化加载GDT表保证开启保护模式寄存器值正常,再设置CR0 PE位,开启保护模式。

这个禁用中断的函数我也写了一个函数,保存关中断前的各个寄存器的状态(eflags等),完成实模式到保护模式切换后恢复到原来的状态,这个函数再后面的也可以用到。函数中我也用到了内联汇编函数 sti cli进行开关中断.

加载kernel到内存地址1M

在loader中实现LAB读取磁盘,(一次两字节读取)(通常512字节一次性读取)

#define SYS_KERNEL_LOAD_ADDR		(1024*1024)		// 内核加载的起始地址(此时打开了A20地址线和保护模式,可以访问1M以上空间
static void read_disk(int sector, int sector_count, uint8_t * buf)
名称含义单位
sector起始扇区号(LBA)扇区(512 字节)
sector_count连续读取的扇区数量扇区(512 字节)

创建kernel文件夹,同样cmake设置起始位置1M
在这里插入图片描述
在这里插入图片描述
栈的作用:C的局部变量,函数调用中的参数
在保护模式下 push pop的一个栈都是4个字节
esp是栈顶指针 ebp相对比较固定。ESP 指向当前栈顶,EBP 保存的是上一个调用帧的基地址(上一个函数的栈底基准)

我在loader32.c中写了 ((void (*)(boot_info_t *))kernel_entry)(&boot_info); 然后进入kernel1M地址,

		push %ebpmov %esp, %ebpmov 0x8(%ebp), %eaxpush %eaxcall kernel_init

取出boot_info参数传给kernel_init函数void kernel_init (boot_info_t * boot_info)(函数指针) 我没用全局变量不依赖任何外部状态,bootloader 和 kernel解耦,可移植性强。

我再讲解下这个loader的这个函数指针:(为了跳到kernel_entry(裸地址0x100000)并传入参数boot_info

部分解释
void (*)(boot_info_t *)一个函数指针类型,指向接受一个 boot_info_t * 参数、返回 void 的函数
(void (*)(boot_info_t *))kernel_entrykernel_entry 强制转换为这种函数指针类型
((...))(&boot_info)把这个函数指针当成函数调用,传入参数 &boot_info

加载内核映像文件

在这里插入图片描述
改一下kernel.lds和loader32.c中跳转到kernel的函数指针的裸地址改一下0x100000 改为0x1000

elf

elf更小,并且可以进行权限设置。
elf是一种通用的可执行文件格式,操作系统内核是程序逻辑,elf是其封装形式,方便bootloader加载执行。
这个elf文件是我的c、汇编写的内核编译出来的最终可执行映像。
在这里插入图片描述
查看手册把elf_header和program_header的结构放在elf.h中
在loader32.c中写了一个加载elf的函数,首先要进行魔术验证,0x7F ‘E’ ‘L’ 'F’开头,判断其是否合法。将elf里面的内容提取到指定的物理地址,做好初始化(比如bss通常是未初始化的参数,初始化为0),最后跳转到elf的入口地址(return elf_hdr->e_entry;)。然后将e_entry这个elf入口地址作为loader的跳转地址(刚刚我们改的裸地址就可以改成e_entry)
从而跳转到kernel内核。
在这里插入图片描述

一些基础知识

链接脚本与代码数据段

先看一个具体的
在这里插入图片描述
有下面的一个顺序
在这里插入图片描述
为什么会这么放呢?
下面是一个测试
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
当我们建立了kernel.lds就不用在每个文件夹下的cmake -text进行设置 告诉编译器这些分别放在哪
在这里插入图片描述

创建GDT表

段描述符 结构的主要是和cpu有关,就放在kernel的include文件下cpu.h里面。
GDT表就放在cpu.c中

段页式内存管理

在这里插入图片描述
在这里插入图片描述

我们先关注分段存储
在这里插入图片描述
段页式内存管理第一部分:逻辑地址 → 线性地址
我采用的是平坦模型(base address = 0,offset的limit可以覆盖整个4GB空间),选择子在GDT查表得到基地址,然后和偏移量相加得到线性地址。
换而言之:线性地址 = offset (因为 base = 0)
在这里插入图片描述
但是我仍需要设置GDT表:

gdt:(参考下面段描述符).quad 0                       ; 空描述符.quad 0x00CF9A000000FFFF      ; 代码段:base=0, limit=4GB.quad 0x00CF92000000FFFF      ; 数据段:base=0, limit=4GB

其中limit有20位(高 4 + 低 16),虽然 limit 字段最大只能表示 0xFFFFF(1MB),但段描述符中还有一个 “粒度位(G bit)”,它决定 limit 的单位是字节还是 4KB。如果是4KB*1MB=4GB 因此段限长可以设置为4GB,从而实现平坦系统。

总结一下内存访问整体流程:
在这里插入图片描述

下面是对段描述符,GDT结构 处理上的详细介绍:

段描述符:
在这里插入图片描述
在这里插入图片描述

/*** GDT描述符(其实就是段描述符)*/
typedef struct _segment_desc_t {uint16_t limit15_0;uint16_t base15_0;uint8_t base23_16;uint16_t attr;uint8_t base31_24;
}segment_desc_t;

主要分成三个部分limit base attr

/*** 设置段描述符*/
void segment_desc_set(int selector, uint32_t base, uint32_t limit, uint16_t attr) {segment_desc_t * desc = gdt_table + (selector >> 3);// 如果界限比较长,将长度单位换成4KBif (limit > 0xfffff) {attr |= 0x8000;limit /= 0x1000;}desc->limit15_0 = limit & 0xffff;desc->base15_0 = base & 0xffff;desc->base23_16 = (base >> 16) & 0xff;desc->attr = attr | (((limit >> 16) & 0xf) << 8);desc->base31_24 = (base >> 24) & 0xff;
}/*** 然后利用set函数初始化GDT*/
void init_gdt(void) {// 全部清空for (int i = 0; i < GDT_TABLE_SIZE; i++) {segment_desc_set(i << 3, 0, 0, 0);}//数据段segment_desc_set(KERNEL_SELECTOR_DS, 0x00000000, 0xFFFFFFFF,SEG_P_PRESENT | SEG_DPL0 | SEG_S_NORMAL | SEG_TYPE_DATA| SEG_TYPE_RW | SEG_D | SEG_G);// 只能用非一致代码段,以便通过调用门更改当前任务的CPL执行关键的资源访问操作segment_desc_set(KERNEL_SELECTOR_CS, 0x00000000, 0xFFFFFFFF,SEG_P_PRESENT | SEG_DPL0 | SEG_S_NORMAL | SEG_TYPE_CODE| SEG_TYPE_RW | SEG_D | SEG_G);// 调用门gate_desc_set((gate_desc_t *)(gdt_table + (SELECTOR_SYSCALL >> 3)),KERNEL_SELECTOR_CS,(uint32_t)exception_handler_syscall,GATE_P_PRESENT | GATE_DPL3 | GATE_TYPE_SYSCALL | SYSCALL_PARAM_COUNT);// 加载gdtlgdt((uint32_t)gdt_table, sizeof(gdt_table));
}

其中selector >> 3才能表示GDT的索引,因为选择子selector 是16位,结构包括 Index(段描述符索引)、TI(表选择标志1位)和 RPL(请求者特权级2位),所以通过选择子查找GDT中的索引需要右移3位。

有的同学可能会疑惑,进入保护模式CS/DS/SS等端寄存器不是从16位变成32位了吗?
事实上每个段寄存器(如CS、DS、SS、ES、FS、GS)在保护模式下实际上分为两部分,其中“隐藏部分”,保存了段基址、限长、权限等完整段描述符信息,这部分可能是32 位甚至 64 位。

部分说明
可见部分16 位选择子(selector)
隐藏部分段描述符缓存(base、limit、access rights)

显示字符串

bios中断向量表中来显示

0x10

内联汇编来显示

也是用的bios中断进行显示的

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/news/917107.shtml
繁体地址,请注明出处:http://hk.pswp.cn/news/917107.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【WRF-Chem第二期】WRF-Chem有关 namelist 详解

目录namelist 选项&#xff1a;chem_opt 的选择其他化学相关的 namelist 选项气溶胶光学属性与输出边界与初始条件配置&#xff08;气体&#xff09;参考本博客详细介绍 WRF-Chem有关 namelist 选项。 namelist 选项&#xff1a;chem_opt 的选择 chem_opt 是什么&#xff1f;…

STM32-USART串口实现接收数据三种方法(1.根据\r\n标志符、2.空闲帧中断、3.根据定时器辅助接收)

本章概述思维导图&#xff1a;USART串口初始化配置串口初始化配置在&#xff08;STM32-USART串口初始化章节有详细教程配置&#xff09;&#xff0c;本章不做讲解直接代码示例&#xff0c;本章重点在于串口实现接收数据三种方法&#xff1b;配置USART1串口接收初始化函数步骤&a…

【NLP舆情分析】基于python微博舆情分析可视化系统(flask+pandas+echarts) 视频教程 - 微博评论数据可视化分析-点赞区间折线图实现

大家好&#xff0c;我是java1234_小锋老师&#xff0c;最近写了一套【NLP舆情分析】基于python微博舆情分析可视化系统(flaskpandasecharts)视频教程&#xff0c;持续更新中&#xff0c;计划月底更新完&#xff0c;感谢支持。今天讲解微博评论数据可视化分析-点赞区间折线图实现…

Unity_SRP Batcher

SRP Batcher 全面解析&#xff1a;原理、启用、优化与调试一、什么是 SRP Batcher&#xff1f;SRP Batcher 是 Unity Scriptable Render Pipeline&#xff08;URP、HDRP 或自定义 SRP&#xff09; 专属的 CPU 渲染性能优化技术&#xff0c;核心目标是 减少材质切换时的 CPU 开销…

详解Vite 配置中的代理功能

在前端开发过程中&#xff0c;你可能经常会遇到一个头疼的问题&#xff1a;当你在本地启动的前端项目中调用后端接口时&#xff0c;浏览器控制台会报出类似 “Access to fetch at ‘http://xxx’ from origin ‘http://localhost:3000’ has been blocked by CORS policy” 的错…

理解梯度在神经网络中的应用

梯度&#xff08;Gradient&#xff09;是微积分中的一个重要概念&#xff0c;广泛应用于机器学习和深度学习中&#xff0c;尤其是在神经网络的训练过程中。下面将从梯度的基本概念、其在神经网络中的应用两个方面进行详细介绍。一、梯度的基本概念 1.1 什么是梯度&#xff1f; …

WPF,按钮透明背景实现MouseEnter

在帮手程序&#xff08;assister.exe&#xff09;中&#xff0c;可以点击录制按钮&#xff0c;实现录制用户操作直接生成操作列表。而在弹出录制按钮的悬浮窗中&#xff0c;需要能够拖动录制按钮放置在任意的位置&#xff0c;以免阻挡正常的窗口。具体功能是&#xff0c;当鼠标…

【抄袭】思科交换机DAI(动态ARP监控)配置测试

一.概述 1.DAI作用 ①.使用DAI&#xff0c;管理员可以指定交换机的端口为信任和非信任端口&#xff1a; 信任端口可以转发任何ARP信息 非信任端口的ARP消息要进行ARP检测验证 ②.交换机执行如下的ARP验证&#xff1a; 静态ARP监控&#xff1a;为一个静态的IP地址配置一个静态AR…

在嵌入式系统或 STM32 平台中常见的外设芯片和接口

在嵌入式系统或 STM32 平台中常见的 外设芯片 或 模块名称&#xff0c;包括&#xff1a; &#x1f4fa; 显示驱动&#xff08;如 ST7735、OTM8009A、NT35510&#xff09;&#x1f4f7; 摄像头模组&#xff08;如 OV5640、OV9655、S5K5CAG&#xff09;&#x1f4be; Flash 存储器…

AI 类型的 IDE

指集成了 AI 辅助编程能力的集成开发环境 一、代码辅助生成 ✅ 自动补全&#xff08;更智能&#xff09; 比传统 IDE 更智能&#xff0c;理解上下文&#xff0c;生成整个函数/模块 示例&#xff1a;根据函数名 calculateTax 自动生成税务计算逻辑 ✅ 函数 / 类自动生成 给…

JP3-3-MyClub后台后端(一)

Java道经 - 项目 - MyClub - 后台后端&#xff08;一&#xff09; 传送门&#xff1a;JP3-1-MyClub项目简介 传送门&#xff1a;JP3-2-MyClub公共服务 传送门&#xff1a;JP3-3-MyClub后台后端&#xff08;一&#xff09; 传送门&#xff1a;JP3-3-MyClub后台后端&#xff08;…

架构实战——互联网架构模板(“存储层”技术)

目录 一、SQL 二、NoSQL 三、小文件存储 四、大文件存储 本文来源:极客时间vip课程笔记 一、SQL SQL 即我们通常所说的关系数据。前几年 NoSQL 火了一阵子,很多人都理解为 NoSQL 是完全抛弃关系数据,全部采用非关系型数据。但经过几年的试验后,大家发现关系数据不可能完全被…

CentOS7.9在线部署Dify

一、CentOS7.9安装dify 二、检查是否安装dcoker docker --version2.1下载后将安装包上传至服务器对应文件夹下,我选在放在了 /root文件夹下 cd /root2.2 上传至服务器 cd /root #对应目录下tar -xvf docker-26.1.4.tgz # 解压安装包:chmod 755 -R docker # 赋予可执…

深入浅出C语言指针:从数组到函数指针的进阶之路(中)

指针是C语言的灵魂&#xff0c;也是初学者最头疼的知识点。它像一把锋利的刀&#xff0c;用得好能大幅提升代码效率&#xff0c;用不好则会让程序漏洞百出。今天这篇文章&#xff0c;我们从数组与指针的基础关系讲起&#xff0c;一步步揭开指针进阶类型的神秘面纱&#xff0c;最…

java web Cookie处理

java web 设置cookie更改启动端口// Directory tree (5 levels) ├── src\ │ ├── a.txt │ └── com\ │ └── zhang\ │ └── ServletContext\ │ ├── cookie\ │ └── servletContext.java └── web\├─…

机器学习—线性回归

一线性回归线性回归是利用数理统计中回归分析&#xff0c;来确定两种或两种以上变量间相互依赖的定量关系的一种统计分析方法。相关关系&#xff1a;包含因果关系和平行关系因果关系&#xff1a;回归分析【原因引起结果&#xff0c;需要明确自变量和因变量】平行关系&#xff1…

Spring Boot Admin 监控模块笔记-实现全链路追踪

一、概述Spring Boot Admin&#xff08;SBA&#xff09;是一个用于监控和管理 Spring Boot 应用程序的工具。它提供了一个 Web 界面&#xff0c;可以集中管理多个 Spring Boot 应用程序的健康状态、指标、日志、配置等信息。通过 SBA&#xff0c;你可以轻松地监控和管理你的微服…

容器化与Docker核心原理

目录 专栏介绍 作者与平台 您将学到什么&#xff1f; 学习特色 容器化与Docker核心原理 引言&#xff1a;为什么容器化成为云计算时代的基石&#xff1f; 容器化技术全景与Docker核心原理&#xff1a;从概念到实践 文章摘要 1. 引言&#xff1a;为什么容器化成为云计算…

掌握Python三大语句:顺序、条件与循环

PS不好意思各位&#xff0c;由于最近笔者在参加全国大学生电子设计大赛&#xff0c;所以最近会出现停更的情况&#xff0c;望大家谅解&#xff0c;比赛结束后我会加大力度&#xff0c;火速讲Python的知识给大家写完&#x1f396;️&#x1f396;️&#x1f396;️&#x1f396;…

JAVA结合AI

Java 与人工智能&#xff08;AI&#xff09;的结合正经历从技术探索到深度融合的关键阶段。以下从技术生态、应用场景、工具创新、行业实践及未来趋势五个维度展开分析&#xff0c;结合最新技术动态与企业级案例&#xff0c;揭示 Java 在 AI 时代的独特价值与发展路径。一、技术…