分页机制小记

       其实分页机制不难,但是不做记录的话又感觉会忘,所以还是做点记录。

       分页的主要目的在于实现虚拟存储器。线性地址中任意一个页都能映射到物理地址中的任何一个页,这使得内存管理变得相当灵活。

看图:在这里插入图片描述
       在未打开分页机制时,线性地址等同于物理地址,于是可以认为,逻辑地址通过分段机制直接转换成物理地址。但当分页开启时,分段机制将逻辑地址转换成线性地址,线性地址再通过分页机制转换成物理地址。

       线性地址通过分页机制进行转换时,先是从由寄存器cr3指定的页目录中根据线性地址的高10位得到页表地址,然后在页表中根据线性地址的第12到21位得到物理页首地址,将这个首地址加上线性地址低12位便得到了物理地址。

       分页机制是否生效的开关位于cr0的最高位PG位。如果PG=1,则分页机制生效。当我们准备好了页目录表和页表,并将cr3指向页目录表之后,只需要置PG位,分页机制就开始工作了。

       关于页目录(PDE),页表和cr3这里就不记录了,网上的资料一大堆,主要是看对页目录和页表以及cr3的操作。

       下面就通过改变地址映射关系执行同一个线性地址处的模块得到两次不同结果这个例子来体会分页机制。

1.分页机制的启动

代码:

; 启动分页机制 --------------------------------------------------------------
SetupPaging:
	; 根据内存大小计算应初始化多少PDE以及多少页表
	xor	edx, edx
	mov	eax, [dwMemSize]
	mov	ebx, 400000h	; 400000h = 4M = 4096 * 1024, 一个页表对应的内存大小
	div	ebx
	mov	ecx, eax	; 此时 ecx 为页表的个数,也即 PDE 应该的个数
	test	edx, edx
	jz	.no_remainder
	inc	ecx		; 如果余数不为 0 就需增加一个页表
.no_remainder:
	mov	[PageTableNumber], ecx	; 暂存页表个数

	; 为简化处理, 所有线性地址对应相等的物理地址. 并且不考虑内存空洞.

	; 首先初始化页目录
	mov	ax, SelectorFlatRW
	mov	es, ax
	mov	edi, PageDirBase0	; 此段首地址为 PageDirBase0
	xor	eax, eax
	mov	eax, PageTblBase0 | PG_P  | PG_USU | PG_RWW
.1:
	stosd
	add	eax, 4096		; 为了简化, 所有页表在内存中是连续的.
	loop	.1

	; 再初始化所有页表
	mov	eax, [PageTableNumber]	; 页表个数
	mov	ebx, 1024		; 每个页表 1024 个 PTE
	mul	ebx
	mov	ecx, eax		; PTE个数 = 页表个数 * 1024
	mov	edi, PageTblBase0	; 此段首地址为 PageTblBase0
	xor	eax, eax
	mov	eax, PG_P  | PG_USU | PG_RWW
.2:
	stosd
	add	eax, 4096		; 每一页指向 4K 的空间
	loop	.2

	mov	eax, PageDirBase0
	mov	cr3, eax
	mov	eax, cr0
	or	eax, 80000000h
	mov	cr0, eax
	jmp	short .3
.3:
	nop

	ret
; 分页机制启动完毕 ----------------------------------------------------------

       这段代码第一部分是; 根据内存大小计算应初始化多少PDE以及多少页表,关于这个的具体实现可以参考:根据内存大小计算页表个数,把页表的个数计算好后存储在[PageTableNumber]中,因为PageTableNumber是定义在数据段中的,所以[PageTableNumber]的段是数据段。

       第二部分初始化页目录,填充页目录包括填充页目录的结构和页目录的表项,先填充页目录结构,页目录结构包括页表的基址和一些属性位,将页目录结构放在eax中,通过stosd指令写入内存;页目录的表项就是页表,每个表项4字节长,其中存的就是每个页表的地址,一个页表是4KB(4096)大小。代码中所有页表在内存中是连续的,所以每个表项的值相差4096,注意,是每个表项的值相差4096,不是表项的地址相差4096。

       第三部分初始化页表,每个页表1024个PTE,将PTE个数算出后,先将页表属性写入页表开始地址,然后写入每个PTE项(每个PTE项指向的是一个页的地址,书中每页同样是连续的)。

       页目录和页表初始化完成后,将cr3指向页目录表,然后设置cr3的PG位,这样,分页机制就启动完成了。

2.切换页目录,改变地址映射关系

; 切换页表 ------------------------------------------------------------------
PSwitch:
	; 初始化页目录
	mov	ax, SelectorFlatRW
	mov	es, ax
	mov	edi, PageDirBase1	; 此段首地址为 PageDirBase1
	xor	eax, eax
	mov	eax, PageTblBase1 | PG_P  | PG_USU | PG_RWW
	mov	ecx, [PageTableNumber]
.1:
	stosd
	add	eax, 4096		; 为了简化, 所有页表在内存中是连续的.
	loop	.1

	; 再初始化所有页表
	mov	eax, [PageTableNumber]	; 页表个数
	mov	ebx, 1024		; 每个页表 1024 个 PTE
	mul	ebx
	mov	ecx, eax		; PTE个数 = 页表个数 * 1024
	mov	edi, PageTblBase1	; 此段首地址为 PageTblBase1
	xor	eax, eax
	mov	eax, PG_P  | PG_USU | PG_RWW
.2:
	stosd
	add	eax, 4096		; 每一页指向 4K 的空间
	loop	.2

	; 在此假设内存是大于 8M 的
	mov	eax, LinearAddrDemo
	shr	eax, 22
	mov	ebx, 4096
	mul	ebx
	mov	ecx, eax
	mov	eax, LinearAddrDemo
	shr	eax, 12
	and	eax, 03FFh	; 1111111111b (10 bits)
	mov	ebx, 4
	mul	ebx
	add	eax, ecx
	add	eax, PageTblBase1
	mov	dword [es:eax], ProcBar | PG_P | PG_USU | PG_RWW

	mov	eax, PageDirBase1
	mov	cr3, eax
	jmp	short .3
.3:
	nop

	ret
; ---------------------------------------------------------------------------

       这里初始化页目录和页表和启动分页机制一样,主要的代码就是这段:

	; 在此假设内存是大于 8M 的
	mov	eax, LinearAddrDemo              ;LinearAddrDemo	equ	00401000h
	shr	eax, 22
	mov	ebx, 4096
	mul	ebx
	mov	ecx, eax
	mov	eax, LinearAddrDemo
	shr	eax, 12
	and	eax, 03FFh	; 1111111111b (10 bits)
	mov	ebx, 4
	mul	ebx
	add	eax, ecx
	add	eax, PageTblBase1         ;PageTblBase1	equ	211000h	; 页表开始地址:2M + 64K + 4K
	mov	dword [es:eax], ProcBar | PG_P | PG_USU | PG_RWW

       这段代码是改变线性地址LinearAddrDemo对应的物理地址的语句。改变后,LinearAddrDemo将不再对应ProcFoo,而是对应ProcBar。
在这里插入图片描述
       线性地址的各部分功能如图所示,

	mov	eax, LinearAddrDemo
	shr	eax, 22

       取线性地址的高10位,即页表在PDE(页目录表)中的索引,

	mov	ebx, 4096
	mul	ebx
	mov	ecx, eax

       因为一个页表是4K大小,所以乘以4096来算出对应页表的地址,

	mov	eax, LinearAddrDemo
	shr	eax, 12
	and	eax, 03FFh	; 1111111111b (10 bits)

       取中间10位,即对应页在页表中的表项号,

	mov	ebx, 4
	mul	ebx
	add	eax, ecx
	add	eax, PageTblBase1

       因为每页大小4K,所以乘以4,这算出来是一个偏移,然后再加上页表的首地址就算出要修改的地址。

	mov	dword [es:eax], ProcBar | PG_P | PG_USU | PG_RWW

       将这个地址写入ProcBar就成功修改了线性地址LinearAddrDemo对应的物理地址。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值