Gos —— 启动分页机制

16 篇文章 0 订阅

写在前面:自制操作系统Gos 第二章第七篇:主要内容是如何获取开启x86体系下的分页机制
相关页表的概念在程序地址转换这篇博客中已经详细介绍过了,本篇文章将不会重复介绍
Gos完整代码:Github

页目录项和页表项

想要启动分页机制,我们首先要谈的就是存储物理页地址的页目录项和页表项了。其都是4字节,这32位被人为的规定成为如下表示:
(守约可以说的上是操作系统设计的精髓了)
在这里插入图片描述
在这里插入图片描述
可以明显的看出来页表项和页目录项的差别其实就在于第7位,其也叫PAT位,它指定了这个32位的空间是页表的物理地址还是物理页的地址。
除此之外,其他位也被赋予了不同的含义:

  • P:存在位,其表示该页是否存在于物理内存
  • RW:读写位,表示此页是否可读写
  • US:权限位,如果是1表示这个进程归属于User级,特权级0~3均可访问;如果是0表示Supervisor级,特权级0~2可以访问
  • PWT:页级写透位,标识这个不仅是普通内存,还是高速缓存,可以用于改善读写速率,Gos用不上这个
  • PCD:页级高速缓存禁止位,如果是1标识该页启用高速缓存,为0标识禁止将该页缓存
  • A:访问位,若为1标识该页被CPU访问过了,这个是由CPU设置的
  • D:脏页位,也是CPU去设置,当CPU对一个页面执行写操作时,就会设置对应页表项的D位为1
  • G:全局位,为了提高获取物理地址的速度,将虚拟地址于物理地址转换结果存储在TLB中。G为1标识该页为全局页,会一直被保存在TLB表中。
  • AVL:可用位,标识操作系统、软件是否可用该页
  • 地址32~12位:这里只有20位,这是因为页表其实就是4KB大小了。

开启分页机制

现在我们已经掌握了页目录表和页表的的创建规则了。但是,页目录表其本身跟描述符表是一样的,是个内存中的数据结构。CPU想要访问它就必须有个专门的寄存器来存储其结构,这就是控制寄存器cr0~cr7。其中cr3寄存器便存储了页目录表的起始物理地址,所以其又被称为页目录基址寄存器PDBR。其结构如下:
在这里插入图片描述
而我们要开启分页机制,其实就是将页目录表的物理地址的高20位写入cr3寄存器就可以了。

而打开分页机制的开关其实我们早就接触过了,其就是cr0寄存器的PG位,将其置为 1 就可以了。
在这里插入图片描述
总结一下如何开启分页机制:

  1. 准备好页目录表以及页表
  2. 将页目录表的地址写入控制寄存器cr3
  3. 寄存器 cr0的 PG位置为1

设计页表

在开启分页机制之前,其实我们还有一件事情没有做,那就是规划页表啦!

页表的基本思想就是共享,共享于操作系统和用户进程之间,这就要求我们将其置于内核的1G空间中。话不多说,我们上代码:

首先我们要做的第一件事情就是将页目录表中的内容清空。为什么呢?你想想你每次开机之后,系统都没有运行进程是吧。这其实就是在开机自启动的时候创建了一个空的页目录表PDE。

;先把页目录占用的空间逐字节清0
;这里会循环4096=4KB次,将每个字节都清空
;PAGE_DIR_TABLE_POS指的是页目录表的基址
   mov ecx, 4096
   mov esi, 0
.clear_page_dir:
   mov byte [PAGE_DIR_TABLE_POS + esi], 0
   inc esi
   loop .clear_page_dir

之后呢,我们要做的事情就是创建页目录表PDE啦,这个页目录项我们的目标是按照Linux那样分布,也就是3G空间属于自己,1G空间属于内核。按照每个页目录项表示4MB的空间,那么其实内核和用户进程的分界线就是第768项

而我们这里设计页目录表和页表紧邻,规定页目录表的起始位置是0x100000,那么页表的起始位置就是0x101000。那么我们要做的第一件事情就是把0x101000写入页目录表的第一项和第768项。之后使页目录表的最后一项指向页目录表的地址,这样使方便我们拿到页目录表的地址

:页目录项每个元素占4字节,那么整个页目录表就是占4*1024=4096字节,转换为16进制就是0x1000

这样之后,页目录表中的内容如下:
在这里插入图片描述

;开始创建页目录项(PDE)
.create_pde:				     ; 创建Page Directory Entry
   mov eax, PAGE_DIR_TABLE_POS
   add eax, 0x1000 			     ; 此时eax为第一个页表的位置及属性,0项要指向实际物理内存的基址位置
   mov ebx, eax				     ; 此处为ebx赋值,是为.create_pte做准备,ebx为基址。

;   下面将页目录项00xc00都存为第一个页表的地址,
;   一个页表可表示4MB内存,这样0xc03fffff以下的地址和0x003fffff以下的地址都指向相同的页表,
;   这是为将地址映射为内核地址做准备
   or eax, PG_US_U | PG_RW_W | PG_P	     ; 页目录项的属性RW和P位为1,US为1,表示用户属性,所有特权级别都可以访问.
   mov [PAGE_DIR_TABLE_POS + 0x0], eax       ;1个目录项,在页目录表中的第1个目录项写入第一个页表的位置(0x101000)及属性(3)
   mov [PAGE_DIR_TABLE_POS + 0xc00], eax     ; 一个页表项占用4字节,0xc00表示第768个页表占用的目录项,0xc00以上的目录项用于内核空间,
					     ; 也就是页表的0xc0000000~0xffffffff共计1G属于内核,0x0~0xbfffffff共计3G属于用户进程.
   sub eax, 0x1000
   mov [PAGE_DIR_TABLE_POS + 4092], eax	     ; 使最后一个目录项指向页目录表自己的地址

之后,我们要做的事情就是填充一张页表的256项中的内容了。这里为什么是256项呢,其实我们主要就是因为我们的内核放在哪里这个问题。我们这里的策略就是将内核放在低端1M内存中。所以我们要填充这256项,至于其他的页表项,那当然是由用户进程来填充啦。

;下面创建页表项(PTE)
   mov ecx, 256				     ; 1M低端内存 / 每页大小4k = 256
   mov esi, 0
   mov edx, PG_US_U | PG_RW_W | PG_P	     ; 属性为7,US=1,RW=1,P=1
.create_pte:				     ; 创建Page Table Entry
   mov [ebx+esi*4],edx			     ; 此时的ebx已经在上面通过eax赋值为0x101000,也就是第一个页表的地址 
   add edx,4096
   inc esi
   loop .create_pte

这样之后,我们的内存布局又变了。主要是页表中的会变成如下内容:
在这里插入图片描述
好了,现在我们的内核程序有了地方存放了。但是我们还有一件事情没有做哦!
那就是初始化我们内核要用到的内存空间,也就是页目录表的769~1024项,这部分我们需要将其的P、RW以及U三位都置为1并且给他们分配页表。这个页表的分配范围便是从第二张页表之后总共256个页表,每个页表可用表示4M的空间,总共就是1G内核空间啦。

;创建内核其它页表的PDE
   mov eax, PAGE_DIR_TABLE_POS
   add eax, 0x2000 		     ; 此时eax为第二个页表的位置
   or eax, PG_US_U | PG_RW_W | PG_P  ; 页目录项的属性RW和P位为1,US为0
   mov ebx, PAGE_DIR_TABLE_POS
   mov ecx, 254			     ; 范围为第769~1022的所有目录项数量
   mov esi, 769
.create_kernel_pde:
   mov [ebx+esi*4], eax
   inc esi
   add eax, 0x1000
   loop .create_kernel_pde
   ret

这样之后呢,我们的内存中又出现变化啦,现在的内存布局如下:
在这里插入图片描述

写入cr3

现在我们的页表已经设计好了,那么其实我们就可以进入第二步了。非常简单:

   ; 把页目录地址赋给cr3
   mov eax, PAGE_DIR_TABLE_POS
   mov cr3, eax

开启分页

这一步也简单的不得了,就是把cr0寄存器的pg位置为1就可以了。

   ; 打开cr0的pg位(31)
   mov eax, cr0
   or eax, 0x80000000
   mov cr0, eax

至此,我们便进入到了分页机制了。完整的代码在loader.S文件中,之后的博客中我会逐步解析全部的loader.S

参考文献

[1] 操作系统真相还原
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

shenmingik

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值