查看系列文章点这里: 操作系统真象还原
前言
现代 CPU 都采用的虚拟内存空间技术,其功能是依靠硬件实现的,但是它所需要的某些数据仍然需要我们进行设置,本章就来看看实现最简单的虚拟内存,我们需要做些什么。
一、如何开启分页机制?
在进入保护模式之后,我们需要做的第一件事,就是开启分页机制,让操作系统可以使用虚拟地址。开启的方法非常简单,和进入保护一样,分为三步:
- 准备好页目录表及页表;
- 将页目录表地址写入控制寄存器CR3;
- 将寄存器CR0的PG位置1;
是不是跟进入保护模式的步骤非常相似,第二步就是告诉CPU这些个表在内存的哪里,第三步就是打开分页机制。重点是第一步,如何设计物理内存与虚拟内存直接的映射关系,咱们下一节慢慢说。
如果大家不了解什么是虚拟内存,什么是分页机制,建议先搜来学习一下,不然可能会云里雾里的。
二、如何设计页目录和页表?
因为我们是自己设计操作系统,所以在内存管理方面,我们完全可以按照我们自己的想法来做。我们知道一个用户进程必须要有操作系统配合,才能在计算机上运行起来,也就是操作系统必须要在内存中才行,不然用户进程就找不到操作系统。
我们一共有4GB的虚拟空间,为了简单,我们干脆直接将3GB ~ 4GB这部分,固定划分给操作系统,任何用户进程想要找到操作系统,就直接访问3GB ~ 4GB这段虚拟地址就行。当然,我们自己实现的操作系统远远没有这么大,这么分配完全是为了图个方便。
在这个基础上,页目录的设计就简单了,将操作系统内核所在的上1GB虚拟内存(3GB~4GB)映射到物理内存0~1GB,这部分很简单不用特别强调,主要有两个地方可以需要特别说明一下:
第一就是,第0个页目录项与第768个页目录项指向同一个页表,其目的是为了保证 LOADER 在分页机制开启后,仍能正常运行。第二就是最后一个,也就是第1023个目录项,其所对应的内存不是内核,而是页页目录表,这是为了我们修改页目录表。
其它就没什么了,让我们来实践检验一下!
三、实践检验!
首先是 boot.inc 中一些新增常量的定义,如下:
------------------------------------
; 页目录表属性
;------------------------------------
; 页目录表的起始地址
PAGE_DIR_TABLE_POS equ 0x100000
;------------------------------------
; 页表属性
;------------------------------------
; present 位 -> 表示页是否存在于内存中
PG_P equ 1b
; 读写位
PG_RW_R equ 00b
PG_RW_W equ 10b
; 用户和根位 -> 是否可以被特权级别为 3 的程序访问
PG_US_S equ 000b
PG_US_U equ 100b
loader.S 新增的部分如下:
;============================================================
;启动分页模式
;============================================================
;第一步:创建页目录表并初始化页内存位图
call setup_page
;修改视频(显示)内存段的段描述符中的段基址,以反映当前的虚拟地址。
;sgdt指令将全局描述符表寄存器(GDTR)的内容存储到指定地址的内存中
sgdt [gdt_ptr]
;gdt_ptr + 2 是 GDT 的地址
mov ebx, [gdt_ptr+2]
;ebx+0x18+4 是第三个段描述符(视频内存段描述符)的高4字节地址
;与0xc0000000进行OR操作是为了修改这个段描述符的最高字节,并将视频内存段映射到4GB的上1GB(即内核空间)
or dword [ebx+0x18+4], 0xc0000000
;修改GDT本身的基址
add dword [gdt_ptr+2], 0xc0000000
add esp, 0xc0000000
;第二步:将PDT的地址放入CR3寄存器
mov eax, PAGE_DIR_TABLE_POS
mov cr3, eax
;第三步:将CR0寄存器中的pg位(第31位)置为1
mov eax, cr0
or eax, 0x80000000
mov cr0, eax
;在开启分页后,用GDT新的地址值重新加载
lgdt [gdt_ptr]
mov byte [gs:162], 'V'
mov byte [gs:0x280], '5'
mov byte [gs:0x281], 0xA4
mov byte [gs:0x282], ' '
mov byte [gs:0x283], 0xA4
mov byte [gs:0x284], 'O'
mov byte [gs:0x285], 0xA4
mov byte [gs:0x286], 'P'
mov byte [gs:0x287], 0xA4
mov byte [gs:0x288], 'E'
mov byte [gs:0x289], 0xA4
mov byte [gs:0x28a], 'N'
mov byte [gs:0x28b], 0xA4
mov byte [gs:0x28c], ' '
mov byte [gs:0x28d], 0xA4
mov byte [gs:0x28e], 'P'
mov byte [gs:0x28f], 0xA4
mov byte [gs:0x290], 'A'
mov byte [gs:0x291], 0xA4
mov byte [gs:0x292], 'G'
mov byte [gs:0x293], 0xA4
mov byte [gs:0x294], 'E'
mov byte [gs:0x295], 0xA4
jmp $
; ============================================================
; 创建页目录及页表
; ============================================================
;逐字清除占用页面目录的4KB字节
setup_page:
mov ecx, 4096
mov esi, 0
.clear_PDT:
mov byte [PAGE_DIR_TABLE_POS+esi], 0 ;全部初始化为0
inc esi
loop .clear_PDT
;------------------------
;创建页目录项(PDE 1、768、1024)
;------------------------
.create_PDE:
mov eax, PAGE_DIR_TABLE_POS
;页目录表(PDT)从0x100000开始,其本身占用0x1000字节。因此,第一个页表位于地址0x101000
add eax, 0x1000
mov ebx, eax
or eax, PG_US_U | PG_RW_W | PG_P
;创建第1个页目录项(PDE)
mov [PAGE_DIR_TABLE_POS + 0x0], eax
;创建第768个页目录项(PDE) -> 目的是将虚拟地址3GB (0xc0000000)~3GB+4MB (0xc03fffff)映射到第一个页表,然后将其映射到物理地址0~4MB,即第一个标准页面。
mov [PAGE_DIR_TABLE_POS + 0xc00], eax
;让最后一个页目录项存储PDT的起始地址,为了能动态操作页表
sub eax, 0x1000
mov [PAGE_DIR_TABLE_POS+4092], eax
;------------------------
;创建页表项(PTE)
;------------------------
;一个完整的页表对应于4MB的物理内存,但内核只需要1MB(256 * 4KB)的空间。因此,首先只创建了256个页表项。
mov ecx, 256
mov esi, 0
mov edx, PG_US_U | PG_RW_W | PG_P
.create_PTE:
mov [ebx+esi*4], edx
;一个页表项对应4KB的物理内存,所以要加4096。
add edx, 4096
inc esi
loop .create_PTE
;------------------------
; 为操作系统内核创建页目录项(PDE 769~1022)
;------------------------
;将操作系统内核所在的上1GB虚拟内存(3GB~4GB)映射到物理内存0~1GB。
mov eax, PAGE_DIR_TABLE_POS
add eax, 0x2000 ;EAX代表页表的地址,指向第二个页表位置
or eax, PG_US_U | PG_RW_W | PG_P
mov ebx, PAGE_DIR_TABLE_POS
mov ecx, 254 ;一共创建254 PDEs
mov esi, 769 ;从第769项开始
.create_kernel_PDE:
mov [ebx+esi*4], eax
inc esi
add eax, 0x1000
loop .create_kernel_PDE
ret
编译、链接、运行!
接下来我们来检查一下,我们内存映射是否正确,输入info gdt与info tab查看GDT与内存映射关系,如下图:
如果你的结果和我是一样的,那就说明正确啦,大家可以思考一下为什么这样就是正确的的,尤其是内存映射!
持续更新中~~