欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 文旅 > 游戏 > 打开分页机制

打开分页机制

2025/4/21 3:35:08 来源:https://blog.csdn.net/weixin_45131087/article/details/144411594  浏览:    关键词:打开分页机制

分页机制的表

Snipaste_2024-12-09_21-15-53

Snipaste_2024-12-09_21-19-39

一般线性地址和物理地址大小不会一样,物理内存空间不够时候,涉及和外部磁盘的swap过程,但是这个系统不涉及

CR3放的是页表的起始地址

Snipaste_2024-12-09_21-21-04

代码部分

Snipaste_2024-12-11_20-13-26

PDE:4MB page 一级页表的页块大小为4MB

然后是这个二级页表 PTE:4KB page

关于什么是PDE,什么是PTE?

img

一级页表的代码

我们这个OS采用的就是一级页表,页块大小为4MB

Snipaste_2024-12-11_21-23-05

os.c

#define PDE_P			(1 << 0)
#define PDE_W			(1 << 1)
#define PDE_U			(1 << 2)
#define PDE_PS			(1 << 7)uint32_t pg_dir[1024] __attribute__((aligned(4096))) = {[0] = (0) | PDE_P | PDE_PS | PDE_W | PDE_U,	    // PDE_PS,开启4MB的页,恒等映射
};

image-20241211202756773

属性含义:
P:有效位。0 表示当前表项无效。
R/W: 0 表示只读。1表示可读写。
U/S: 0 表示只能0、1、2特权级可访问。3 表示只有特权级程序可访问
A: 0 表示该页未被访问,1表示已被访问。
D: 脏位。0表示该页未写过,1表示该页被写过。
PS: 只存在于页目录表。0表示这是4KB页,指向一个页表。1表示这是4MB大页,直接指向物理页。
PWT、PCD、G:暂不讲解

首先要设置这个数组的第0个表项

Snipaste_2024-12-11_20-37-09

这一块代码是从0地址处开始填入的,需要占用实际的多个分页,当我们开启分页机制,相当于开启了虚拟内存,那么CPU就不能直接看到OS的内核部分代码了,所以内核这块要恒等映射

之所以用4MB的页块大小也是为了方便起见,那我们内核代码这地方做恒等映射也更方便了,只用给一个页块初始化就行了

[0] = (0) | PDE_P | PDE_PS | PDE_W | PDE_U

开启分页的机制

Start.S

// 跳转到c语言中运行
call os_init// 将一级页表的地址给到,pg_dir前面os.c里面定义的数组
mov $pg_dir, %eax // eax中转
mov %eax, %cr3mov %cr4, %eax
orl $(1 << 4), %eax			// 配置CR4的PSE位,使其支持4MB分页
mov %eax, %cr4mov %cr0, %eax   // 需要将CR0的最高位置1
orl $(1 << 31), %eax		// 打开PG位,开启分页机制
mov %eax, %cr0

Snipaste_2024-12-11_21-02-54

看看CR3有没有变成pg_dir的地址

Snipaste_2024-12-11_21-03-45

Snipaste_2024-12-11_21-05-02

假如分页成功开启的话输入info mem,就会是下图这样

Snipaste_2024-12-11_21-05-34

第一段是普通数据的映射,第二段是OS内核的映射

普通数据的映射

对于内核数据我们采用的是一级页表映射,对于普通数据我们采用二级映射

os.c

static uint32_t pg_table[1024] __attribute__((aligned(4096))) = {PDE_U};    // 要给个值,否则其实始化值不确定(编译链接的原因)

如果不给非0初值的话,字段全部混乱了

Snipaste_2024-12-11_22-08-38

因为这个数组会被放在一个叫bss的区域,在我们生成的os.bin文件中是不占内存空间的,被加载到内存中时,内存中这个数组原地方是什么值,我们读的就是什么值,所以比较随机,给0索引这个地方一个非0值,编译器就会自动给后面跟随着的数组项全0

因为是高10位索引页目录表,我们pg_dir数组里面的的索引为虚拟地址的高10位,数组项内容为对应二级页表的起始地址,我们这里只设置了一个二级页表就是page_table,但是编译器不支持直接将page_table的地址直接填进去,所以必须通过代码进行初始化

os_init

这个函数需要在汇编中调用

#define MAP_ADDR        (0x80000000)            // 要映射的地址void os_init (void) {// 虚拟内存// 0x80000000开始的4MB区域的映射pg_dir[MAP_ADDR >> 22] = (uint32_t)pg_table | PDE_P | PDE_W | PDE_U;//这段代码完成了下图2的工作pg_table[(MAP_ADDR >> 12) & 0x3FF] = (uint32_t)map_phy_buffer| PDE_P | PDE_W | PDE_U; //这段代码完成了下图3的工作
};

(MAP_ADDR >> 12) & 0x3FF:就是取中间的10位

Snipaste_2024-12-11_21-37-49

我们上面代码使用的是针对4KB块的情况

对于我们这段代码的话,pg_dir里面都是采用PDE的样式设置,pg_table里面采用的都是PTE的样式设置

图2

vs code监视器里面看一看两个数组(页表)的情况

Snipaste_2024-12-11_21-51-27

我们再看一下0x80000000这地方的内容

Snipaste_2024-12-11_21-54-24

发现和数组内容一样

我们改一下数组的0索引地方的值

Snipaste_2024-12-11_21-55-06

Snipaste_2024-12-11_21-55-38

Snipaste_2024-12-11_21-55-55

发现0x80000000这地方的内容随之改变

但是我们知道,我们定义数组的这块os.c是属于4MB范围内的,也就是下图红色框框出的部分

Snipaste_2024-12-11_20-37-09

我们在这地方采用的是一级页表那种的恒等映射,又因为0x80000000这个地址是超过4MB的,所以我们做这个实验的目的是想表达一个物理地址空间可以被多个虚拟地址所指向

Snipaste_2024-12-11_22-06-14

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词