操作系统(Linux Kernel 0.11Linux Kernel 0.12)一手资料解读整理——引导启动程序(Boot)之内存的映射机制

server/2025/1/22 6:23:32/

前言

上电启动流程如下:

boot/目录下的三个汇编语言文件(bootsect.S 和 setup.S 是实模式下运行的 16位代码程序,采用近似于 Intel汇编语言语法,并且需要使用 8086 汇编编译器和连接器 as86 和 1d86。而 head.s 则使用一种 AT&T 的汇编语法格式,并且运行在保护模式下,需要用 GNU 的 as(gas)汇编器进行编译)。

操作系统第一阶段如下:

(剖析操作系统,一开始就是面向芯片编程,按照芯片手册,当涉及了进程调度则面向多任务处理)

操作系统说白了就是在内存中放置各种数据结构来实现“管理”的功能

内核对内存的管理机制主要在于分段分页机制

虚拟地址到物理地址的转换是由硬件完成的,而页目录表和页表是由操作系统负责管理的

分段

程序员在代码中给出一个内存地址(应用程序所使用的是逻辑地址),在保护模式下要先经过分段机制的转换,才能最终变成物理地址(当没有开启分页机制)

内存分段中,一个程序的逻辑地址通过分段机制自动地映射(变换)到中间层的4GB(2^32)线性地址空间中。程序每次对内存的引用都是对内存段中内存的引用。当程序引用一个内存地址时,通过把相应的段基址加到程序员看得见的逻辑地址上就形成了一个对应的线性地址。此时若没有启用分页机制,则该线性地址就被送到 CPU的外部地址总线上,用于直接寻址对应的物理内存

CPU进行地址变换(映射)的主要目的是为了解决虚拟内存空间到物理内存空间的映射问题。虚拟内存空间的含义是指一种利用二级或外部存储空间,使程序能不受实际物理内存量限制而使用内存的一种方法。通常虚拟内存空间要比实际物理内存量大得多。

分页

setup_paging 这个标签处的代码开启分页机制

setup_paging:mov ecx,1024*5xor eax,eaxxor edi,edipushfcldrep stosdmov eax,_pg_dirmov [eax],pg0+7mov [eax+4],pg1+7mov [eax+8],pg2+7mov [eax+12],pg3+7mov edi,pg3+4092mov eax,00fff007hstd
L3: stosdsub eax,00001000hjge L3popfxor eax,eaxmov cr3,eaxmov eax,cr0or  eax,80000000hmov cr0,eaxret

(这段代码,就是把页表和页目录表在内存中写好,之后开启 cr0 寄存器的分页开关)

 linux-0.11 认为,总共可以使用的内存不会超过 16M,也即最大地址空间为 0xFFFFFF。 而按照当前的页目录表和页表这种机制,1 个页目录表最多包含 1024 个页目录项(也就是 1024 个页表),1 个页表最多包含 1024 个页表项(也就是 1024 个页),1 页为 4KB(因为有 12 位偏移地址),因此,16M 的地址空间可以用 1 个页目录表 + 4 个页表搞定。

4(页表数)* 1024(页表项数) * 4KB(一页大小)= 16MB

所以,主要完成将页目录表放在内存地址的最开头

_pg_dir 标签    之后紧挨着这个页目录表,放置 4 个页表

.org 0x1000 pg0:
.org 0x2000 pg1:
.org 0x3000 pg2:
.org 0x4000 pg3:
.org 0x5000

最终将页目录表和页表填写好数值,来覆盖整个 16MB 的内存。随后,开启分页机制。此时内存中的页表相关的布局如下。

  

(这些页目录表和页表放到了整个内存布局中最开头的位置,就是覆盖了开头的 system 代码了,不过被覆盖的 system 代码已经执行过了,所以无所谓)

同时也需要通过一个寄存器告诉 CPU 把这些页表放在了哪里

xor eax,eax
mov cr3,eax

(相当于告诉 cr3 寄存器,0 地址处就是页目录表,再通过页目录表可以找到所有的页表,也就相当于 CPU 知道了分页机制的全貌了。)

 CPU 首先把线性地址被拆分成        高 10 位:中间 10 位:后 12 位

高 10 位负责在页目录表中找到一个页目录项,这个页目录项的值加上中间 10 位拼接后的地址去页表中去寻找一个页表项,这个页表项的值,再加上后 12 位偏移地址,就是最终的物理地址。 

而这一切的操作,都由计算机的一个硬件叫 MMU,中文名字叫内存管理单元,有时也叫 PMMU,分页内存管理单元。由这个部件来负责将虚拟地址(线性地址)转换为物理地址。这种页表方案叫做二级页表,第一级叫页目录表 PDE,第二级叫页表 PTE。(所以整个过程不用过多介入,作为操作系统这个软件层,只需要提供好页目录表和页表即可)

开启分页机制的开关。其实就是更改 cr0 寄存器中的一位(31 位)

 页目录表和页表的具体结构如下

setup_paging:...mov eax,_pg_dirmov [eax],pg0+7mov [eax+4],pg1+7mov [eax+8],pg2+7mov [eax+12],pg3+7mov edi,pg3+4092mov eax,00fff007hstd
L3: stosdsub eax, 1000hjpe L3...

(前五行表示,页目录表的前 4 个页目录项,分别指向 4 个页表)

比如页目录项中的第一项 [eax] 被赋值为 pg0+7,也就是 0x00001007,根据页目录项的格式,表示页表地址为 0x1000,页属性为 0x07 表示改页存在、用户可读写。
后面几行表示,填充 4 个页表的每一项,一共 4*1024=4096 项,依次映射到内存的前 16MB 空间。

(每2^12B (4KB) 的物理空间需要一个页表项,每个页表项需要4KB。所以不管物理内存多大,页表项装满时总是要占用大约千分之一的内存;4个页表,每个页表1024项,所以4个页表装满需要占用16MB的空间)

1个页目录表最多包含1024个页目录项(也就是1024个页表),但是16M的地址空间所对应的这个页目录表中只包含了4个页目录项(也就是4个页表)

经过这套分页机制,线性地址将恰好和最终转换的物理地址一样,如下图:

(存在一点错误的结构图,计算的应该是13M而非15M)

地址转换过程

Intel 体系结构的内存管理可以分成两大部分,分段分页。 分段机制,其目的是为了为每个程序或任务提供单独的代码段(cs)、数据段(ds)、栈段(ss),使其不会相互干扰。 分页机制,开机后分页机制默认是关闭状态,一般需要手动开启,并且设置好页目录表(PDE)和页表(PTE)。其目的在于可以按需使用物理内存,同时也可以在多任务时起到隔离的作用。 在 Intel 的保护模式下,分段机制是没有开启和关闭一说的,它必须存在,而分页机制是可以选择开启或关闭的。

逻辑地址:程序员写代码时给出的地址叫逻辑地址,其中包含段选择子偏移地址两部分。

线性地址:通过分段机制,将逻辑地址转换后的地址,叫做线性地址。而这个线性地址是有个范围的,这个范围就叫做线性地址空间,32 位模式下,线性地址空间就是 4G。

物理地址:就是真正在内存中的地址,它也是有范围的,叫做物理地址空间。那这个范围的大小,就取决于你的内存有多大了。

虚拟地址:如果没有开启分页机制,那么线性地址就和物理地址是一一对应的,可以理解为相等。如果开启了分页机制,那么线性地址将被视为虚拟地址,这个虚拟地址将会通过分页机制的转换,最终转换成物理地址。

Intel的一手芯片手册中,逻辑地址到线性地址再到物理地址的转换过程如下

Intel手册的内存管理原文是如下描述的

启用了分页机制的内存布局:

(此时的栈顶指针地址,应该是在system所在位置的内存里)

之后便是跳到内核的main函数进行操作系统的具体工作,诸如与内存相关的规划内存(内存边界划分)及更加细致的内存管理(内存分配相关的数据结构的使用)

对于操作系统main函数中的内存框架部分,将在未来另外整理一篇!!!!!!


http://www.ppmy.cn/server/160382.html

相关文章

apache-zeppelin 命令执行 (CNVD-2019-33156)

目录 1、漏洞描述 2、访问映射80端口 3、点击Create New Note,执行Linux反弹,选择sh,并创建note 4、执行反弹命令,并点击运行 5、公网服务器监听并成功反射 1、漏洞描述 Apache Zeppelin是一款基于Web的NoteBook,支持交互式数…

rocketmq dashboard 安装

下载源码 下载并解压,切换至源码目录 rocketmq-dashboard-master/ ① 编译 rocketmq-dashboard $ mvn clean package -Dmaven.test.skiptrue 如果服务器没有mvn环境,可以下载在本地,通过idea打包也是一样的。 配置 在jar包同级目录创建一…

【探索前端技术之 React Three.js—— 简单的人脸动捕与 3D 模型表情同步应用】

大家好,我是智界工具库,致力于分享好用实用且智能的软件以及在JAVA语言开发中遇到的问题,如果本篇文章对你有所帮助请帮我点个小赞小收藏吧,谢谢喲!😘😘😘 简介🌈&…

Leetcode:2239

1,题目 2,思路 循环遍历满足条件就记录,最后返回结果值 3,代码 public class Leetcode2239 {public static void main(String[] args) {System.out.println(new Solution2239().findClosestNumber(new int[]{-4, -2, 1, 4, 8})…

TiDB 的优势与劣势

TiDB 的优势与劣势 TiDB 作为一款新兴的分布式数据库,在业界逐渐崭露头角。它兼具传统关系型数据库的特性,又充分利用分布式架构的优势。那么,TiDB 究竟有怎样的优缺点呢?今天我们来聊聊 TiDB 的优势与劣势,帮你全面了…

Y3编辑器2.0功能指引

文章目录 一、2.0功能概览1.1 地形1.1.1 植被染色1.1.2 3D物理组件和逻辑物理组件染色1.1.3 悬崖创建时自动刷纹理 1.2 成就系统1.3 新ECA1.4 界面拦截和可拖动1.5 音频上限及优先级 二、界面编辑器:元件(待补)三、AIGC3.1 语音生成3.2 图片生…

Kotlin语言的正则表达式

Kotlin语言中的正则表达式 引言 正则表达式(Regular Expression,简称Regex)是一种用于匹配字符串中字符组合的工具。在数据处理、文本解析等领域,正则表达式以其强大的字符串处理能力得到了广泛的应用。而Kotlin作为一种现代的编…

STL容器-- list的模拟实现(附源码)

STL容器-- list的模拟实现(附源码) List的实现主要考察我们对list这一容器的理解,和代码的编写能力,通过上节对list容器的使用,我们对list容器已经有了一些基本的了解,接下来就让我们来实现一些list容器常见…