x86使用页表实现虚拟内存原理分析---使用代码分析

news2024/10/4 4:27:59

分页机制

这一部分在手册第四章
视频讲解可以看这一个课程

在不使用分页机制的时候, 我们看到的是物理内存, 物理内存有多大, 我们就可以使用多大的内存

使用内存分页机制, 我们就可以扩充访问的地址范围, 也可以实现权限的细分, 实际上就是实现虚拟内存, 将地址进行映射, 看到的内存更大了, 但是实际上可以使用的内存的大小还是不变的

访问的内存==>从页表里面找物理内存==>访问实际的物理内存

开启以后得访问过程: 根据段寄存器找到对应的记录的GDT表, 之后根据表找到自己的使用的内存, 加上偏移量之后就是实际的地址, 这一个地址会通过分页机制里面的页表去获取实际的物理地址, 页表的地址放在CR3的寄存器里面

这样可以实现用户使用内存的时候感觉是一块连续的内存, 但是实际的内存可能是不连续的

处理器在访问数据、获取指令时,使用的都是线性地址,只要它是连续的就可以了,最终都能够通过映射表找到实际的物理地址。

image-20240204214518458

实现虚拟内存

在使用多任务的时候不同任务感觉自己访问的地址是相同的, 但是实际是不同的, 这是通过页表实现的, 不同任务可以有不同的页表, 把一个相同的地址定位到不同的物理地址
这一个表记录在CR3里面, 在任务切换的时候可以进行切换

使用TSS进行任务切换

实际配置

实际使用的时候可以使用一级页表(每一块是4MB)和二级页表(每一块是4KB大小)

  • 使用4KB的模式, 使用二级页表

image-20230912091325312

使用这一个地址的不同段去获取实际的页表

  • 使用4M的模式: 使用一级页表

image-20240204221949465

会根据传过来的数据的地址(逻辑地址)分段之后进行访问不同的页表, 获得一个4KB的空间的地址, 最后通过偏移量进行实际的访问

这一个表的地址在CR3里面

image-20240204220942889

CR3以及使用4M模式的一级页表格式(只有这一个表, 不需要二级页表)

image-20240204221254066

image-20240204221129884

使用4KB模式的时候的一二级页表格式

image-20240204215743557

使用CR4以及CR0控制实际使用的模式以及页表的开启

image-20240205141932827

这一个位控制使用分页

image-20240204220801499
这一个是实际的打开时候的寄存器状态
image-20240204220257786

使用这一个位开启4MB的模式

image-20240204221645335

同时满足这两个条件的时候可以使用4MB的一级页表

实际的实现

第一级映射(页目录表PDE)有两种的格式, 一种是4MB的映射, 一种是4KB的映射使用4MB模式的时候, 就不需要二级页表了, 只有一个表, 最后可以使用的内存实际上是4MB, 使用4KB模式的时候会使用两级页表, 最后实际控制的内存大小是4GB

第二级映射(页表PTE)

实际实现一级映射(4MB)

需要在打开页表之前实现映射, 否则CPU会找不到对应的内存, 直接映射到0地址的位置

//这个表是否有效
#define PDE_P                   (1<<0)
//是否可写
#define PDE_W                   (1<<1)
//是否可以被低权限访问
#define PDE_U                   (1<<2)
//设置使用的模式(4M模式)
#define PDE_PS                  (1<<7)

//定义一个页表的结构体,需要设置低0的表项
//这一个设置的是逻辑地址0地址的分页, 是一个恒等的映射, 使得代码的访问正常, 映射 的地址还是0
uint32_t pg_dir[1024] __attribute__((aligned(4096))) = {
    [0] = (0) | PDE_P |  PDE_W | PDE_U | PDE_PS;
};

image-20240204220553713

_start_32:
	//在这里设置段地址
	mov $KERNEL_DATA_SEG, %ax
	mov %ax, %ds
	mov %ax, %es
	mov %ax, %ss
	mov %ax, %gs
	mov %ax, %fs
	mov $_start, %esp

	//打开页表, 记录位置
	mov $pg_dir, %eax
	mov %eax, %cr3
	//CR4里面有一个位控制是否允许这一个模式 
	mov %cr4, %eax
	orl $(1<<4), %eax
	mov %eax, %cr4
	//还需要控制PR0最高位w为1
	mov %cr0, %eax
	orl $(1<<31), %eax
	mov %eax, %cr0

	jmp .

image-20230912100752988

image-20230912100916065

分页打开后, 可以使用这一个命令查看映射关系, 权限是u: 用户 r: 读 w: 写

实际实现二级映射

image-20240204223228835

image-20240205143304590

一级表, bit7为0

image-20240205143202219

二级表, bit7为1

//这个表是否有效
#define PDE_P                   (1<<0)
//是否可写
#define PDE_W                   (1<<1)
//是否可以被低权限访问
#define PDE_U                   (1<<2)
//设置使用的模式4KB/4MB
#define PDE_PS                  (1<<7)
//新建另一个映射的地址
#define MAG_ADDR                0x80000000
//使用二级表进行控制内存测试, 这里是实际上的地址
uint8_t map_phy_buffer[4096] __attribute__((aligned(4096))) = {0x36};
//创建一个二级表项,随便给一个值,在后面会进行设置,随便初始化一个值连接器会把其他的位置设置为0,否则会为随机的
static uint32_t page_table[1024] __attribute__((aligned(4096))) = {PDE_U};

//定义一个页表的结构体,需要设置低0的表项
uint32_t pg_dir[1024] __attribute__((aligned(4096))) = {
    [0] = (0) | PDE_P |  PDE_W | PDE_U | PDE_PS,
};

void os_init(void){
    //设置一级表,使用的是表的高10位,这里会找到想要的虚拟地址所在的位置,设置为二级表的位置
    /********************************************************************************/
    //计算一下4KB的话对应的表项 = 二级表项地址+权限(这里没有使用4M的映射(PDE_PS))
    pg_dir[MAG_ADDR>>22] = (uint32_t)page_table | PDE_P | PDE_W | PDE_U;
    /********************************************************************************/
    //初始化表的二级,这里是实际的地址,之后需要设置对应的位置,这里会设置二级表指向的是上面的数组
    page_table[(MAG_ADDR>>12)&0x3ff] = (uint32_t)map_phy_buffer | PDE_P | PDE_W | PDE_U;

}

实际的地址计算

image-20240204223356678

_start_32:
	//在这里设置段地址
	mov $KERNEL_DATA_SEG, %ax
	mov %ax, %ds
	mov %ax, %es
	mov %ax, %ss
	mov %ax, %gs
	mov %ax, %fs
	mov $_start, %esp
	//在这里调用设置4KB的分页表
	call os_init
	# 打开页表机制
	mov $pg_dir, %eax
	mov %eax, %cr3
	//CR4里面有一个位控制是否允许这一个模式(这一个没用上)
	mov %cr4, %eax
	orl $(1<<4), %eax
	mov %eax, %cr4
	//还需要控制PR0最高位w为1
	mov %cr0, %eax
	orl $(1<<31), %eax
	mov %eax, %cr0

	jmp .

image-20240205144130516

使用这一个命令查看现有的映射

image-20230912144435370

image-20230912144527920

在修改之后发现两个位置是同步的, 可以直接操控第二个映射地址或者采用第一个映射的地址

总结

也就是说,在没有开启分页机制时,由程序员给出的逻辑地址,需要先通过分段机制(GDT表)转换成物理地址。但在开启分页机制后,逻辑地址仍然要先通过分段机制进行转换,只不过转换后不再是最终的物理地址,而是线性地址,然后再通过一次分页机制转换,得到最终的物理地址。

GDT表使用

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

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

相关文章

希尔排序算法

目录 ShellSort希尔排序 整体思路 图解分析 【1】预排序 单组排序 多组并排 【2】直接插入排序 关于gap取值 总代码实现 时间复杂度 ShellSort希尔排序 希尔排序法又称缩小增量法。 希尔排序法的基本思想是&#xff1a;先选定一个整数&#xff0c;把待排序文件中所有…

产品经理学习-产品运营《流程管理》

如何进行流程管理 信息可视化 甘特图-流程管理思维导图-方案讨论原型图-活动文档 明确责任制 分工明确&#xff0c;关键环境有主负责人通过时间倒推督促管理 沟通技巧 明确共同利益以结果激励做好信息同步 如何进行监控活动效果 监控活动的效果是要监控数据 活动每个环境的…

MySQL学习记录——십일 索引

文章目录 1、了解索引2、聚簇、非聚簇索引3、操作1、主键索引2、唯一键索引3、普通索引4、注意事项 4、全文索引 1、了解索引 MySQL服务器是在内存中的&#xff0c;所有数据库的CURD操作都是在内存中进行&#xff0c;索引也是如此。索引是用来提高性能的&#xff0c;它通过组织…

[嵌入式系统-16]:RT-Thread -2- 主要功能功能组件详解与API函数说明、API参考手册入口

目录 一、RT-Thread主要功能组件 二、内核组件 2.1 概述 2.2 API 三、设备驱动 3.1 概述 3.2 API 四、通信组件 4.1 概述 4.4 API 五、网络组件 5.1 概述 5.2 API 5.3 补充&#xff1a;MQTT协议 六、文件系统 6.1 概述 6.2 API 七、GUI 组件 7.1 概述 7.2 …

进程终止与进程等待

fork 函数 fork 函数是 Linux 中一个非常重要的函数&#xff0c;它的作用是从已存在的进程中创建一个新进程。这个新进程就是当前进程的子进程。 fork() 函数使用方法&#xff1a;它在头文件 #include <unistd.h> 中&#xff0c;函数原型为 pid_t fork(void); 用一个…

CMNet:Contrastive Magnification Network for Micro-Expression Recognition 阅读笔记

AAAI 2023的一篇文章&#xff0c;东南大学几位老师的工作&#xff0c;用于做微表情识别中的运动增强工作&#xff0c; 以下是阅读时记录的笔记。 摘要&#xff1a; However,existing magnification strategies tend to use the features offacial images that include not onl…

Minio通过Url直接访问附件

1、修改桶策略为public 2、http://locahost:9000/桶名/文件名即可 访问该文件&#xff0c;自己浏览 http://127.0.0.1:9000/netcore/netcore/9d4a526b-a477-46a3-90c7-a668354e3b46.png

C语言每日一题(59)左叶子之和

题目链接 力扣网404 左叶子之和 题目描述 给定二叉树的根节点 root &#xff0c;返回所有左叶子之和。 示例 1&#xff1a; 输入: root [3,9,20,null,null,15,7] 输出: 24 解释: 在这个二叉树中&#xff0c;有两个左叶子&#xff0c;分别是 9 和 15&#xff0c;所以返回 2…

【C++航海王:追寻罗杰的编程之路】vector

目录 1 -> vector的介绍及使用 1.1 -> vector的介绍 1.2 -> vector的使用 1.2.1 -> vector的介绍 1.2.2 -> vector iterator的使用 1.2.3 -> vector空间增长问题 1.2.4 -> vector的增删查改 1.2.5 -> vector迭代器失效问题 2 -> vector的深…

K8s进阶之路-控制器无状态服务:

RC/RS/Deployment 控制器 deployment无状态&#xff08;最常用&#xff09;&#xff1a; nginx和Apache statefulset有状态&#xff1a; mysql和redis damonset初始化 job一次性任务 cronjob任务计划 1无状态&#xff1a;不会对本地环境产生依赖如&#xff1a;nginx和Apache …

Docker详解及使用

文章目录 为什么要用docker为什么会出现容器Docker 是什么容器是什么虚拟化是什么Docker 和 虚拟化的区别Docker 容器有几种在状态什么是仓库什么是镜像什么是容器仓库、镜像、容器的关系常用的 Docker 命令如何把主机的东西拷贝到容器内部如何让容器随着 Docker 服务启动而自动…

八、右侧下部页面内容区域

内容区可直接放置router-view占位子路由展示位置 所有内容区路由是layout的子路由&#xff0c;子路由需要在父路由中使用router-view占位才能显示app.vue是所有其他组件的父view/router&#xff0c;router里的父子关系通过来展现。所以在app.vue也要router-view标签 也可拆成组…

⭐北邮复试刷题LCR 012. 寻找数组的中心下标__前缀和思想 (力扣119经典题变种挑战)

LCR 012. 寻找数组的中心下标 给你一个整数数组 nums &#xff0c;请计算数组的 中心下标 。 数组 中心下标 是数组的一个下标&#xff0c;其左侧所有元素相加的和等于右侧所有元素相加的和。 如果中心下标位于数组最左端&#xff0c;那么左侧数之和视为 0 &#xff0c;因为…

SpringMVC 的参数绑定之list集合、Map

标签中name属性的值就是pojo类的属性名 参数绑定4 list [对象] <form action"teaupd.do" method"post"> <c:forEach items"${list}" var"tea" varStatus "status"> 教师编号&#xff1a;<input…

【SV的面向对象 SV的类_2024.01.02】

OOP术语 OOP&#xff1a;object oriented programming Class类&#xff1a;包含变量和子程序的基本构建块 Object对象&#xff1a;类的一个实例 Handle句柄&#xff1a;指向对象的指针 Property属性&#xff1a;存储数据的变量&#xff1b;在V中&#xff0c;可以是wire或reg类…

⭐北邮复试刷题LCR 034. 验证外星语词典__哈希思想 (力扣119经典题变种挑战)

LCR 034. 验证外星语词典 某种外星语也使用英文小写字母&#xff0c;但可能顺序 order 不同。字母表的顺序&#xff08;order&#xff09;是一些小写字母的排列。 给定一组用外星语书写的单词 words&#xff0c;以及其字母表的顺序 order&#xff0c;只有当给定的单词在这种外…

莱卡云怎么样?简单测评下莱卡云韩国CN2云服务器

莱卡云服务器厂商&#xff0c;国内持证企业服务器商家&#xff0c;运作着香港、美国、韩国、镇江、日本、绍兴、枣庄、等数据中心的云服务器、独立服务器出租、设备托管、CDN等业务。今天为大家带来的是莱卡云韩国CN2服务器的详细评测&#xff0c;该云服务器的数据中心位于韩国…

大语言模型学习路线:从入门到实战

大语言模型学习路线&#xff1a;从入门到实战 在人工智能领域&#xff0c;大语言模型&#xff08;Large Language Models, LLMs&#xff09;正迅速成为一个热点话题。 本学习路线旨在为有基本Python编程和深度学习基础的学习者提供一个清晰、系统的大模型学习指南&#xff0c;…

阿里云服务器“镜像”全方面解析

阿里云服务器镜像怎么选择&#xff1f;云服务器操作系统镜像分为Linux和Windows两大类&#xff0c;Linux可以选择Alibaba Cloud Linux&#xff0c;Windows可以选择Windows Server 2022数据中心版64位中文版&#xff0c;阿里云服务器网aliyunfuwuqi.com来详细说下阿里云服务器操…

辽宁博学优晨教育科技有限公司视频剪辑培训靠谱吗?

在数字媒体日益繁荣的今天&#xff0c;视频剪辑已成为一项炙手可热的技能。不少培训机构纷纷涉足这一领域&#xff0c;辽宁博学优晨教育科技有限公司便是其中之一。然而&#xff0c;面对众多的选择&#xff0c;很多人不禁要问&#xff1a;辽宁博学优晨教育科技有限公司的视频剪…