分頁機制小記

       其實分頁機制不難,但是不做記錄的話又感覺會忘,所以還是做點記錄。

       分頁的主要目的在於實現虛擬存儲器。線性地址中任意一個頁都能映射到物理地址中的任何一個頁,這使得內存管理變得相當靈活。

看圖:在這裏插入圖片描述
       在未打開分頁機制時,線性地址等同於物理地址,於是可以認爲,邏輯地址通過分段機制直接轉換成物理地址。但當分頁開啓時,分段機制將邏輯地址轉換成線性地址,線性地址再通過分頁機制轉換成物理地址。

       線性地址通過分頁機制進行轉換時,先是從由寄存器cr3指定的頁目錄中根據線性地址的高10位得到頁表地址,然後在頁表中根據線性地址的第12到21位得到物理頁首地址,將這個首地址加上線性地址低12位便得到了物理地址。

       分頁機制是否生效的開關位於cr0的最高位PG位。如果PG=1,則分頁機制生效。當我們準備好了頁目錄表和頁表,並將cr3指向頁目錄表之後,只需要置PG位,分頁機制就開始工作了。

       關於頁目錄(PDE),頁表(PTE)和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位,即PTE在PDE中的索引,

	mov	ebx, 4096
	mul	ebx
	mov	ecx, eax

       因爲一個PTE是4K大小,所以乘以4096來算出對應頁表的地址,

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

       取中間10位,即對應頁在PTE中的表項號,

	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對應的物理地址。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章