【自制操做系统05】开启内存分页机制

经过前四章的努力,咱们成功将控制权转交给了 loader.asm 这个程序,而且从实模式跨越到了保护模式。第四章讲保护模式的时候我说过,这是咱们操做系统的第一个精彩之处。但其实这只是针对以前咱们进行的只是无心义的输出,以及硬盘的加载等工做。但到了这一章,以前一步步的努力进入到了保护模式,也只能说是作了不少苦力,其实不少代码都是固定的,给咱们发挥的空间也不大。html

可是到了本章,能够说终于有能体现出咱们设计能力的地方了。git

1、实现分页要作哪些事

仍是先直接简单说要作的事,再说为何,实现分页要作如下三件事:数据结构

  1. 在内存某位置写好页表
  2. 页目录地址赋值给 cr3 寄存器
  3. 将 cr0 寄存器的 pg 位置 1

咱们对比下进入保护模式中实现段描述符机制须要作的三件事:oop

  1. 在内存某位置写好段描述符表
  2. 段描述符表地址赋值给 gdtr 寄存器
  3. 将 cr0 寄存器的 pe 位置 1(这个实际上是开启保护模式)

你看,是不是很是类似呢?都是内存某位置准备xxx,把起始地址赋值给一个特定的寄存器,而后将另外一个特殊寄存器的某位置 1 表示开启。因此上一章我说过,cpu 与操做系体打配合,这种模式运用得很是多。咱们写操做系统的人不用管 cpu 的具体实现,只须要按照指定步骤操做便可,以后硬件会帮咱们完成所须要的功能。学习

2、为何要分页

说实话我也想不明白为何要分页,主要是我说不上来为何不是其余方式,因此这块我也只能跟着官方说的去理解了。操作系统

若是只用段式管理的话,段大小不一致,且同一个程序逻辑地址和物理地址都是连续的。段大小不一致致使内存有大段有小段,也会留下一些内存碎片,过大的段查不进来,太小的段插进去又会产生更小的碎片。同一个段内全部的程序地址都是连续的,这也致使不灵活,咱们但愿能有一套机制使得程序所用的逻辑地址连续,但实际映射到的物理地址并不连续,增长这么一个层来解决这个问题。设计

咱们本讲只是准备一些必要的页表,而后开启页表机制。等到后面多任务的时候才能真正体会到页表的用处以及好处,因此咱们姑且先简单理解下,至于具体的好处,其实有好多细节的,等之后用到的时候慢慢体会。code

3、页表长什么样以及虚拟地址到物理地址的转换

咱们能够类比段的转化,咱们最初给的地址是 段选择子:段内偏移值,在保护模式下,用段选择子去内存中的段描述符表中,找到段描述符,取出段基址,再+段内偏移地址,获得最终的物理地址。htm

页的转化也是相似的,上一步经过段描述符获得的“物理地址”,再开启分页后叫作逻辑地址。这个逻辑地址也是分红 前半部分:后半部分 这种形式,用前半部分的值在页表中寻找并换出一个页地址(也能够理解成基址这个概念),而后再拼接上后半部分的值,获得最终的物理地址。blog

只不过,如今的页表方案通常是二级页表,第一级叫页目录表(PDE),第二级叫页表(PTE)。而后这个逻辑地址就是被当作 高10位:中间10位:后12位。高10位负责再页目录表中找到一个页目录项,这个页目录项的值加上中间10位拼接后的地址去页表中去寻找一个页表项,这个页表项的值,再加上后12位,拼接后的地址就是最终的物理地址。

12位能够表示 4K,因此也就是一个页可表示的内存大小为 4KB。10位能够表示 1K,因此页目录表中最多有 1024 个页目录项,一个页表中最多有 1024 个页表项,那最大可表示的内存范围就是 1024 * 1024 * 4KB = 4G。其实这也是废话,你能够仔细想一想看,不论你分红几级页表,只要是经过这种方式寻址的,只要是一个 32 位的地址,老是能够表示 4G 大小的。只不过经过你的不一样分法,可能致使页大小,页目录项数目,页表数目,以及假如你定了 n 级页表后的 n 级页表的页表项数目不一样而已。

页目录表和页表的数据结构

虚拟地址到物理地址的转换

4、页表设计

咱们这样设计页表:

  • 页目录表的第 0 项和第 768 项,都对应紧接着的第一个页表,映射了低端 1M 的物理内存(0x00000-0x100000),也就是说逻辑地址的开端 1M 和 3G 以上的第一个 1M 地址,都对应这物理内存的地段 1M。
  • 页目录表的第 769~1022 项,分别日后对应 254 个页表,不过这些页表尚未写,先空着
  • 页目录表的第 1023 项,其地址指向该页目录表自己(也就是把页目录表看成页表去理解了),经过这种方式能够访问页目录表自己。(这块其实我也没理解为啥要这么搞,无非就是想用虚拟地址访问到这个页表自己嘛。

为何这样设计呢?

由于咱们分页以前的代码(loader)都在低端 1MB 范围内,因此开启分页以后的逻辑地址开始的 1M 也要一一对应上物理地址的开始 1M,因此有了第 0 个页目录项。第 768 个页目录项对应着逻辑地址 3G 以上的 4M( 0xc0000000~0xc03fffff 不过咱们页表只写了 256 项也就是规划了 1M),这是由于咱们决定将操做系统内核写在 3G 以上的 1M 空间里

咱们规划,虚拟地址的 0~3G 是用户空间,3~4G 是内核空间,因此咱们提早把页目录表的第 769~1022 项建好,至于为何之后再说。

5、上代码

loader.asm

...
;建立页表并初始化(页目录和页表)
PAGE_DIR_TABLE_POS equ 0x100000
call setup_page

;从新加载 gdt,由于已经变成了虚拟地址方式
sgdt [lgdt_value]
mov ebx,[lgdt_value+2]
or dword [ebx+0x18+4],0xc0000000
add dword [lgdt_value+2],0xc0000000
add esp,0xc0000000

;页目录表起始地址存入 cr3 寄存器
mov eax,PAGE_DIR_TABLE_POS
mov cr3,eax

;开启分页
mov eax,cr0
or eax,0x80000000
mov cr0,eax

;从新加载 gdt
lgdt [lgdt_value]

mov byte [gs:0x1e0],'p'
mov byte [gs:0x1e2],'a'
mov byte [gs:0x1e4],'g'
mov byte [gs:0x1e6],'e'
mov byte [gs:0x1ea],'o'
mov byte [gs:0x1ec],'n'

jmp $

setup_page:
;先把页目录占用的空间逐字清零
    mov ecx,4096
    mov esi,0
.clear_page_dir:
    mov byte [PAGE_DIR_TABLE_POS+esi],0
    inc esi
    loop .clear_page_dir
    
;开始建立页目录项(PDE)
.create_pde:
    mov eax,PAGE_DIR_TABLE_POS
    add eax,0x1000; 此时eax为第一个页表的位置及属性
    mov ebx,eax
    or eax,111b
    mov [PAGE_DIR_TABLE_POS],eax
    mov [PAGE_DIR_TABLE_POS+0xc00],eax
    sub eax,0x1000
    mov [PAGE_DIR_TABLE_POS+4*1023],eax

;开始建立页表项(PTE)
    mov ecx,256
    mov esi,0
    mov edx,111b
.create_pte:
    mov [ebx+esi*4],edx
    add edx,4096
    inc esi
    loop .create_pte
    
;建立内核其余页表的页目录项(PDE)
    mov eax,PAGE_DIR_TABLE_POS
    add eax,0x2000
    or eax,111b
    mov ebx,PAGE_DIR_TABLE_POS
    mov ecx,254
    mov esi,769
.create_kernel_pde:
    mov [ebx+esi*4],eax
    inc esi
    add eax,0x1000
    loop .create_kernel_pde
    ret
...

6、运行

Makefile 仍然和上一章同样,因此直接执行 make brun

能够看到分页开启后,成功在屏幕输出了 pageon 字符串

7、学到这的一些感悟

我以前写过一篇文章 究竟什么是技术,还被好多人骂了。我文章里说的就是感受如今作的事情(Java),以及好多好多所谓的技术,都只是应用而已,甚至以为只有基础科学,只有研究质子中子电子,这些东西才算是真正的技术,其余的只是应用而已。

不过如今我知道本身的问题所在了,由于我研究操做系统就是想作点真正的技术。但如今看来,若是还延续当时的想法,像开启分页,进入保护模式,往显卡映射的内存写数据,这些都应该只叫作应用。由于这些的底层原理是 cpu 硬件电路的布线方式,咱们的操做系统只是应用了它们,把一些操做封装起来再暴露给用户而已。

但若是真这样深刻下去,实际上是没完没了的,你的求知欲又会深刻到物理层面,这其实跟计算机技术已经相差甚远了。因此我如今以为,把底层细节看成已知,在这上面创建一套完善的体系,这自己就是这一层的技术了,每一层有每一层技术的复杂性,不能说越底层的才越接近技术,越接近真理。

因此,你能够不断深刻探索底层的技术,但大可没必要对本身所研究层次的知识妄自菲薄。

写在最后:开源项目和课程规划

若是你对自制一个操做系统感兴趣,不妨跟随这个系列课程看下去,甚至加入咱们,一块儿来开发。

参考书籍

《操做系统真相还原》这本书真的赞!强烈推荐

项目开源

项目开源地址:https://gitee.com/sunym1993/flashos

当你看到该文章时,代码可能已经比文章中的又多写了一些部分了。你能够经过提交记录历史来查看历史的代码,我会慢慢梳理提交历史以及项目说明文档,争取给每一课都准备一个可执行的代码。固然文章中的代码也是全的,采用复制粘贴的方式也是彻底能够的。

若是你有兴趣加入这个自制操做系统的大军,也能够在留言区留下您的联系方式,或者在 gitee 私信我您的联系方式。

课程规划

本课程打算出系列课程,我写到哪以为能够写成一篇文章了就写出来分享给你们,最终会完成一个功能全面的操做系统,我以为这是最好的学习操做系统的方式了。因此中间遇到的各类坎也会写进去,若是你能持续跟进,跟着我一块写,必然会有很好的收货。即便没有,交个朋友也是好的哈哈。

目前的系列包括

相关文章
相关标签/搜索