內存分頁機制總結


【0】README(分頁機制)

  • 0.0) source code from orange’s implemention of a os and text description from Zhaojiong's perfect analysis of Linux kernel and for complete code ,please visit https://github.com/pacosonTang/Orange-s-OS/blob/master/p67.asm
  • 0.1)本代碼旨在演示 怎樣開啟分頁機制 + 怎樣構建頁目錄和頁表
  • 0.3)本文 只對 與 分頁機制的 代碼進行簡要注釋,言簡意賅;
  • 0.4) 為了讓廣大小白(像我一樣對os分頁機制不覺明里的小白)真真切切了解分頁機制,即使這篇文章是轉載自 "0.0" 中的兩本,但我還是將本文歸為原創以推薦到博客首頁;(版權,我已在0.0中聲明了)

; ==========================================
; pmtest6.asm
; 編譯方法:nasm pmtest6.asm -o pmtest6.com
; ==========================================

%include	"pm.inc"	; 常量, 宏, 以及一些說明

PageDirBase	 equ	200000h	; 頁目錄開始地址: 2M
PageTblBase	 equ	201000h	; 頁表開始地址: 2M+4K

org	0100h
 jmp	LABEL_BEGIN

[SECTION .gdt]
; GDT
;                            段基址,       段界限, 屬性
LABEL_GDT:           Descriptor 0,              0, 0     ; 空描述符
LABEL_DESC_NORMAL:   Descriptor 0,         0ffffh, DA_DRW	 ; Normal 描述符

; 頁目錄描述符 [add]

LABEL_DESC_PAGE_DIR: Descriptor PageDirBase, 4095, DA_DRW;Page Directory

; 頁表描述符 [add]

LABEL_DESC_PAGE_TBL: Descriptor PageTblBase, 1023, DA_DRW|DA_LIMIT_4K;Page Tables

LABEL_DESC_CODE32:   Descriptor 0, SegCode32Len-1, DA_C+DA_32	 ; 非一致代碼段, 32
LABEL_DESC_CODE16:   Descriptor 0,         0ffffh, DA_C	 ; 非一致代碼段, 16
LABEL_DESC_DATA:     Descriptor 0,      DataLen-1, DA_DRW	 ; Data
LABEL_DESC_STACK:    Descriptor 0,     TopOfStack, DA_DRWA + DA_32	; Stack, 32 位
LABEL_DESC_VIDEO:    Descriptor 0B8000h,   0ffffh, DA_DRW	 ; 顯存首地址
; GDT 結束

GdtLen	 equ	$ - LABEL_GDT	; GDT長度
GdtPtr	 dw	GdtLen - 1	; GDT界限
  dd	0	 ; GDT基地址

; GDT 選擇子
SelectorNormal	 equ	LABEL_DESC_NORMAL	- LABEL_GDT
SelectorPageDir	 equ	LABEL_DESC_PAGE_DIR	- LABEL_GDT
SelectorPageTbl	 equ	LABEL_DESC_PAGE_TBL	- LABEL_GDT
SelectorCode32	 equ	LABEL_DESC_CODE32	- LABEL_GDT
SelectorCode16	 equ	LABEL_DESC_CODE16	- LABEL_GDT
SelectorData	 equ	LABEL_DESC_DATA	 - LABEL_GDT
SelectorStack	 equ	LABEL_DESC_STACK	- LABEL_GDT
SelectorVideo	 equ	LABEL_DESC_VIDEO	- LABEL_GDT
; END of [SECTION .gdt]

[SECTION .data1]	 ; 數據段
ALIGN	32
[BITS	32]
LABEL_DATA:
SPValueInRealMode	dw	0
; 字符串
PMMessage:	 db	"In Protect Mode now. ^-^", 0	; 進入保護模式后顯示此字符串
OffsetPMMessage	 equ	PMMessage - $$
DataLen	 equ	$ - LABEL_DATA
; END of [SECTION .data1]


; 全局堆棧段
[SECTION .gs]
ALIGN	32
[BITS	32]
LABEL_STACK:
 times 512 db 0

TopOfStack	equ	$ - LABEL_STACK - 1

; END of [SECTION .gs]


[SECTION .s16]
[BITS	16]
LABEL_BEGIN:
 mov	ax, cs
 mov	ds, ax
 mov	es, ax
 mov	ss, ax
 mov	sp, 0100h

 mov	[LABEL_GO_BACK_TO_REAL+3], ax
 mov	[SPValueInRealMode], sp

 ; 初始化 16 位代碼段描述符
 mov	ax, cs
 movzx	eax, ax
 shl	eax, 4
 add	eax, LABEL_SEG_CODE16
 mov	word [LABEL_DESC_CODE16 + 2], ax
 shr	eax, 16
 mov	byte [LABEL_DESC_CODE16 + 4], al
 mov	byte [LABEL_DESC_CODE16 + 7], ah

 ; 初始化 32 位代碼段描述符
 xor	eax, eax
 mov	ax, cs
 shl	eax, 4
 add	eax, LABEL_SEG_CODE32
 mov	word [LABEL_DESC_CODE32 + 2], ax
 shr	eax, 16
 mov	byte [LABEL_DESC_CODE32 + 4], al
 mov	byte [LABEL_DESC_CODE32 + 7], ah

 ; 初始化數據段描述符
 xor	eax, eax
 mov	ax, ds
 shl	eax, 4
 add	eax, LABEL_DATA
 mov	word [LABEL_DESC_DATA + 2], ax
 shr	eax, 16
 mov	byte [LABEL_DESC_DATA + 4], al
 mov	byte [LABEL_DESC_DATA + 7], ah

 ; 初始化堆棧段描述符
 xor	eax, eax
 mov	ax, ds
 shl	eax, 4
 add	eax, LABEL_STACK
 mov	word [LABEL_DESC_STACK + 2], ax
 shr	eax, 16
 mov	byte [LABEL_DESC_STACK + 4], al
 mov	byte [LABEL_DESC_STACK + 7], ah

 ; 為加載 GDTR 作准備
 xor	eax, eax
 mov	ax, ds
 shl	eax, 4
 add	eax, LABEL_GDT	 ; eax <- gdt 基地址
 mov	dword [GdtPtr + 2], eax	; [GdtPtr + 2] <- gdt 基地址

 ; 加載 GDTR
 lgdt	[GdtPtr]

 ; 關中斷
 cli

 ; 打開地址線A20
 in	al, 92h
 or	al, 00000010b
 out	92h, al

 ; 准備切換到保護模式
 mov	eax, cr0
 or	eax, 1
 mov	cr0, eax

 ; 真正進入保護模式
 jmp	dword SelectorCode32:0	; 執行這一句會把 SelectorCode32 裝入 cs, 並跳轉到 Code32Selector:0  處

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

LABEL_REAL_ENTRY:	 ; 從保護模式跳回到實模式就到了這里
 mov	ax, cs
 mov	ds, ax
 mov	es, ax
 mov	ss, ax

 mov	sp, [SPValueInRealMode]

 in	al, 92h	 ; ┓
 and	al, 11111101b	; ┣ 關閉 A20 地址線
 out	92h, al	 ; ┛

 sti	 ; 開中斷

 mov	ax, 4c00h	; ┓
 int	21h	 ; ┛回到 DOS
; END of [SECTION .s16]


[SECTION .s32]; 32 位代碼段. 由實模式跳入.
[BITS	32]

LABEL_SEG_CODE32:

; 開啟分頁機制的初始化工作,該指令執行后,cpu即可開啟分頁機制[add]

 call	SetupPaging

 mov	ax, SelectorData
 mov	ds, ax	 ; 數據段選擇子
 mov	ax, SelectorVideo
 mov	gs, ax	 ; 視頻段選擇子

 mov	ax, SelectorStack
 mov	ss, ax	 ; 堆棧段選擇子

 mov	esp, TopOfStack


 ; 下面顯示一個字符串
 mov	ah, 0Ch	 ; 0000: 黑底    1100: 紅字
 xor	esi, esi
 xor	edi, edi
 mov	esi, OffsetPMMessage	; 源數據偏移
 mov	edi, (80 * 10 + 0) * 2	; 目的數據偏移。屏幕第 10 行, 第 0 列。
 cld
.1:
 lodsb
 test	al, al
 jz	.2
 mov	[gs:edi], ax
 add	edi, 2
 jmp	.1
.2:	; 顯示完畢

 ; 到此停止
 jmp	SelectorCode16:0

; [add]啟動分頁機制 --------------------------------------------------------------

SetupPaging:

; 為簡化處理, 所有線性地址對應相等的物理地址.
; 首先初始化頁目錄

 mov	ax, SelectorPageDir	; 此段首地址為 PageDirBase
 mov	es, ax
 mov	ecx, 1024	 ; 共 1K 個表項
 xor	edi, edi
 xor	eax, eax
 mov	eax, PageTblBase | PG_P  | PG_USU | PG_RWW ; PageTblBase equ 201000h 頁表開始地址: 2M+4K

; 這里是在初始化頁目錄中項的內容,即對應頁表的內存地址

.1: 
 stosd 

;stosb, stosw, stosd 把al/ ax/ eax的內容存儲到 es:edi 指向的內存單元中, 該指令執行后,edi自增1

 add	eax, 4096	 

; 為了簡化, 所有頁表在內存中是連續的.,每個頁表占用4k字節空間

 loop	.1

; 初始化頁目錄的項內容 over
; 再初始化所有頁表 (1K 個, 4M 內存空間)的項內容,即頁表的項存儲的是內存地址的高20位地址;

 mov	ax, SelectorPageTbl	; 此段首地址為 PageTblBase
 mov	es, ax
 mov	ecx, 1024 * 1024	; 共 1M 個頁表項, 也即有 1M 個頁 (因為1k個頁目錄項,每個目錄項映射到1k個頁表項)
 xor	edi, edi
 xor	eax, eax
 mov	eax, PG_P  | PG_USU | PG_RWW
.2:
 stosd
 add	eax, 4096	 ; 每一頁指向 4K 的空間
 loop	.2

; 初始化頁表的項內容 over

 mov	eax, PageDirBase

; 加載頁目錄的基地址到 cr3

 mov	cr3, eax

; 設置cr0的PG位=1,開啟分頁機制

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

 ret

; 分頁機制啟動完畢 ----------------------------------------------------------

SegCode32Len	equ	$ - LABEL_SEG_CODE32
; END of [SECTION .s32]


; 16 位代碼段. 由 32 位代碼段跳入, 跳出后到實模式
[SECTION .s16code]
ALIGN	32
[BITS	16]
LABEL_SEG_CODE16:
 ; 跳回實模式:
 mov	ax, SelectorNormal
 mov	ds, ax
 mov	es, ax
 mov	fs, ax
 mov	gs, ax
 mov	ss, ax

 mov	eax, cr0
 and	eax, 7FFFFFFEh	 ; PE=0, PG=0
 mov	cr0, eax

LABEL_GO_BACK_TO_REAL:
 jmp	0:LABEL_REAL_ENTRY	; 段地址會在程序開始處被設置成正確的值

Code16Len	equ	$ - LABEL_SEG_CODE16

; END of [SECTION .s16code]

##**3.3 頁式存儲總結** 分段機制將邏輯地址轉換成線性地址,線性地址通過分頁機制轉換成物理地址。 ![這里寫圖片描述](http://img.blog.csdn.net/20150908171505552) **為什么使用分頁?(干貨)** 分頁管理機制的目的在於實現虛擬存儲器,線性地址中的任意一個頁都能映射到物理地址中的任何一個頁,使得內存管理機制特別靈活。 **分頁和分段的最大不同之處在於?(干貨)**
  • 1)段的長度不固定: 段的長度通常與存放在其中的代碼和數據結構具有相同的長度;
  • 2)頁面長度固定:頁面具有固定的長度;
  • 3)如果僅使用分段地址轉換:那么存儲在物理內存中的一個數據結構將包含所有的部分;
  • 4)如果使用了分頁,那么一個數據結構就可以一部分存儲在物理內存中,一部分存儲在磁盤上(虛擬存儲器);
  • 5)為了減少地址轉換所要求的總線總起數量,最近訪問的頁目錄和頁表會被存放在處理器的緩沖器件中,該緩沖器件被稱為TLB(translation lookaside buffer,翻譯后備緩沖寄存器),提高訪存效率;

3.3.1 分頁機制概述

這里寫圖片描述
Why-為什么使用兩極頁表結構?

  • w1)頁表含有2^20(1M)個表項,每項占4字節。如果用一個表來存儲的話,將最多占用4M;為減少內存占用量,X86使用了兩極頁表;

  • w2)每個,僅有一個頁目錄占用4k, 每個頁表占用4k,而要知道頁目錄一定常駐內存,而頁表是在需要的時候才占用內容空間,當然常用的頁表會存儲在TLB中,這在一定程度上減少了
    頁表機制進行線性地址與物理地址映射所占用內存空間;(干貨)

  • 1)第一級頁表——頁目錄(bit31~22):頁目錄項存儲的內容是頁表的基地址(高20位存儲頁表基地址,低12位存儲所指向的頁表屬性)(干貨); 它被存放在1頁4k頁面中, 含有1k個4字節長度的表項,通過線性地址的bit31~22進行索引頁目錄表項;

  • 2)第二級頁表——頁表(bit21~12):頁表項存儲的內容是物理頁的基地址(高20位存儲物理頁基地址,低12位存儲物理頁屬性)(干貨); 它被存放在1頁4k頁面中,含有1k個4字節長度的表項,而該頁表的表項由 bit21~12進行定位;

  • 3)偏移地址(bit11~0):它存儲的是物理頁的第12位地址(干貨); 把頁表項存儲的高20位地址作為物理頁的高20位地址,而偏移地址12位作為物理頁的低12位地址,這樣就得到了32位的物理地址;

  • 4)映射到的物理地址空間:頁長4k,總共有1k個頁目錄項,每個頁目錄項映射一張頁表,每張頁表有1k個頁表項;故總共1k * 1k =1M個頁表項;所以物理地址空間=1M * 4k = 4G

(you should know):
Y1)頁目錄的基地址存儲在cr3中;
Y2)開啟分頁機制,要設置cr0的最高位PG位=1;


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM