操作系统分页C语言代码,操作系统-X86系统上的内存分页

一.x86系列处理器上的页式内存管理

1.硬件层直接支持内存分页机制

2.默认情况下不使用分页机制(段式内存管理)

3.分页机制(进行配置)启动后,使用二级页表对内存进行管理

x86系列处理器的分页方式(32位)

d1f93db72c78c4a29c7fe4641f56ea9d.png

如图所示,32位被分成了三部分

1.在低12位中,表示的是页内偏移地址

2.在中间10位,用于在子页表中查找目标页地址

3.在最高的10位中,用于子页目录中查找页表地址

A.下面由一张图来展示分页机制

59fd416c7c7a5fcc7fbd83d61c5f6a8c.png

B.x86系列处理器的分页方式(32位)

1.在页目录大小中,2的十次方项,每项4字节,一共为4K

2.在子页表大小中,2的十次方项,每项4字节,一共为4K

3.在页大小中,2的十二次方,一共为4K

由上可以得出

1.页目录占用1内存页-可访问1024个子页表

2.单个子页表占用1内存页-可访问1024个页面

3.页面起始地址按4K字节对齐-总是4096整数倍

4.分页后可访问的虚拟内存空间为:4K(1024*1024)=4G

X86简单的分页构建方式

fb6fb13f066e48feec28a718e65db726.png

可以通过for循环构建目录,子页表,主要原因是一个一个的生成的

C.x86系列处理器上的页属性

7fd7ed871899d4b724c3d68bf198b34c.png

1.由于物理页面的地址必须按照4K字节对齐

2.由此可得,页目录可使用地址的低12位进行属性描述

在x86系列处理器上查看页属性的说明

543df31a6c9bc5d59ef3ab13190a9408.png

D.x86对分页的硬件支持--代码上

如下所示

1.将页目录起始地址放置到cr3-该寄存器类似指针指向页目录起始地址

2.将cr0里面的值取出放置到eax寄存器中,

3.将cr0里面的值所对应二进制最高位置1--硬件级开启分页机制

025fee656822803ab2f215fe7a06e1e7.png

在这里需要注意的是

1.loop指令-该指令表示的是循环指令

mov ax,0

mov cx,10

Label:

add ax,cx

loop Label

在这里表示是将cx减1,对cx进行判断,若cx不为0,则执行标签处Label的代码

2.-stosb/stosw/stosd

表示的是把al/ax/eax中的值存储到edi指向的内存单元中,同时edi的值根据方向标志增加或减少(cld/std)

mov es,ax

mov edi,

mov eax,0XFF

cld

stosd

编程实现

loader.asm与inc.asm的设置

%include "inc.asm"

PageDirBase equ 0x200000

PageTblBase equ 0x201000

org 0x9000

jmp ENTRY_SEGMENT

[section .gdt]

; GDT definition

; 段基址, 段界限, 段属性

GDT_ENTRY : Descriptor 0, 0, 0

CODE32_DESC : Descriptor 0, Code32SegLen - 1, DA_C + DA_32

VIDEO_DESC : Descriptor 0xB8000, 0x07FFF, DA_DRWA + DA_32

DATA32_DESC : Descriptor 0, Data32SegLen - 1, DA_DRW + DA_32

STACK32_DESC : Descriptor 0, TopOfStack32, DA_DRW + DA_32

PAGE_DIR_DESC : Descriptor PageDirBase, 4095, DA_DRW + DA_32

PAGE_TBL_DESC : Descriptor PageTblBase, 1023, DA_DRW + DA_LIMIT_4K + DA_32

; GDT end

GdtLen equ $ - GDT_ENTRY

GdtPtr:

dw GdtLen - 1

dd 0

; GDT Selector

Code32Selector equ (0x0001 << 3) + SA_TIG + SA_RPL0

VideoSelector equ (0x0002 << 3) + SA_TIG + SA_RPL0

Data32Selector equ (0x0003 << 3) + SA_TIG + SA_RPL0

Stack32Selector equ (0x0004 << 3) + SA_TIG + SA_RPL0

PageDirSelector equ (0x0005 << 3) + SA_TIG + SA_RPL0

PageTblSelector equ (0x0006 << 3) + SA_TIG + SA_RPL0

; end of [section .gdt]

TopOfStack16 equ 0x7c00

[section .dat]

[bits 32]

DATA32_SEGMENT:

DTOS db "D.T.OS!", 0

DTOS_OFFSET equ DTOS - $$

HELLO_WORLD db "Hello World!", 0

HELLO_WORLD_OFFSET equ HELLO_WORLD - $$

Data32SegLen equ $ - DATA32_SEGMENT

[section .s16]

[bits 16]

ENTRY_SEGMENT:

mov ax, cs

mov ds, ax

mov es, ax

mov ss, ax

mov sp, TopOfStack16

; initialize GDT for 32 bits code segment

mov esi, CODE32_SEGMENT

mov edi, CODE32_DESC

call InitDescItem

mov esi, DATA32_SEGMENT

mov edi, DATA32_DESC

call InitDescItem

mov esi, STACK32_SEGMENT

mov edi, STACK32_DESC

call InitDescItem

; initialize GDT pointer struct

mov eax, 0

mov ax, ds

shl eax, 4

add eax, GDT_ENTRY

mov dword [GdtPtr + 2], eax

; 1. load GDT

lgdt [GdtPtr]

; 2. close interrupt

cli

; 3. open A20

in al, 0x92

or al, 00000010b

out 0x92, al

; 4. enter protect mode

mov eax, cr0

or eax, 0x01

mov cr0, eax

; 5. jump to 32 bits code

jmp dword Code32Selector : 0

; esi --> code segment label

; edi --> descriptor label

InitDescItem:

push eax

mov eax, 0

mov ax, cs

shl eax, 4

add eax, esi

mov word [edi + 2], ax

shr eax, 16

mov byte [edi + 4], al

mov byte [edi + 7], ah

pop eax

ret

[section .s32]

[bits 32]

CODE32_SEGMENT:

mov ax, VideoSelector

mov gs, ax

mov ax, Stack32Selector

mov ss, ax

mov eax, TopOfStack32

mov esp, eax

mov ax, Data32Selector

mov ds, ax

mov ebp, DTOS_OFFSET

mov bx, 0x0C

mov dh, 12

mov dl, 33

call PrintString

mov ebp, HELLO_WORLD_OFFSET

mov bx, 0x0C

mov dh, 13

mov dl, 31

call PrintString

call SetupPage

jmp $

;

;

SetupPage:

push eax

push ecx

push edi

push es

mov ax, PageDirSelector

mov es, ax

mov ecx, 1024 ; 1K sub page tables

mov edi, 0

mov eax, PageTblBase | PG_P | PG_USU | PG_RWW

cld

stdir:

stosd

add eax, 4096

loop stdir

mov ax, PageTblSelector

mov es, ax

mov ecx, 1024 * 1024 ; 1M pages

mov edi, 0

mov eax, PG_P | PG_USU | PG_RWW

cld

sttbl:

stosd

add eax, 4096

loop sttbl

mov eax, PageDirBase

mov cr3, eax

mov eax, cr0

or eax, 0x80000000

mov cr0, eax

pop es

pop edi

pop ecx

pop eax

ret

; ds:ebp --> string address

; bx --> attribute

; dx --> dh : row, dl : col

PrintString:

push ebp

push eax

push edi

push cx

push dx

print:

mov cl, [ds:ebp]

cmp cl, 0

je end

mov eax, 80

mul dh

add al, dl

shl eax, 1

mov edi, eax

mov ah, bl

mov al, cl

mov [gs:edi], ax

inc ebp

inc dl

jmp print

end:

pop dx

pop cx

pop edi

pop eax

pop ebp

ret

Code32SegLen equ $ - CODE32_SEGMENT

[section .gs]

[bits 32]

STACK32_SEGMENT:

times 1024 * 4 db 0

Stack32SegLen equ $ - STACK32_SEGMENT

TopOfStack32 equ Stack32SegLen - 1

; Segment Attribute

DA_32 equ 0x4000

DA_LIMIT_4K EQU 0x8000

DA_DR equ 0x90

DA_DRW equ 0x92

DA_DRWA equ 0x93

DA_C equ 0x98

DA_CR equ 0x9A

DA_CCO equ 0x9C

DA_CCOR equ 0x9E

; Segment Privilege

DA_DPL0 equ 0x00 ; DPL = 0

DA_DPL1 equ 0x20 ; DPL = 1

DA_DPL2 equ 0x40 ; DPL = 2

DA_DPL3 equ 0x60 ; DPL = 3

; Special Attribute

DA_LDT equ 0x82

DA_TaskGate equ 0x85 ; 任务门类型值

DA_386TSS equ 0x89 ; 可用 386 任务状态段类型值

DA_386CGate equ 0x8C ; 386 调用门类型值

DA_386IGate equ 0x8E ; 386 中断门类型值

DA_386TGate equ 0x8F ; 386 陷阱门类型值

; Selector Attribute

SA_RPL0 equ 0

SA_RPL1 equ 1

SA_RPL2 equ 2

SA_RPL3 equ 3

SA_TIG equ 0

SA_TIL equ 4

PG_P equ 1 ; 页存在属性位

PG_RWR equ 0 ; R/W 属性位值, 读/执行

PG_RWW equ 2 ; R/W 属性位值, 读/写/执行

PG_USS equ 0 ; U/S 属性位值, 系统级

PG_USU equ 4 ; U/S 属性位值, 用户级

; 描述符

; usage: Descriptor Base, Limit, Attr

; Base: dd

; Limit: dd (low 20 bits available)

; Attr: dw (lower 4 bits of higher byte are always 0)

%macro Descriptor 3 ; 段基址, 段界限, 段属性

dw %2 & 0xFFFF ; 段界限1

dw %1 & 0xFFFF ; 段基址1

db (%1 >> 16) & 0xFF ; 段基址2

dw ((%2 >> 8) & 0xF00) | (%3 & 0xF0FF) ; 属性1 + 段界限2 + 属性2

db (%1 >> 24) & 0xFF ; 段基址3

%endmacro ; 共 8 字节

; 门

; usage: Gate Selector, Offset, DCount, Attr

; Selector: dw

; Offset: dd

; DCount: db

; Attr: db

%macro Gate 4

dw (%2 & 0xFFFF) ; 偏移地址1

dw %1 ; 选择子

dw (%3 & 0x1F) | ((%4 << 8) & 0xFF00) ; 属性

dw ((%2 >> 16) & 0xFFFF) ; 偏移地址2

%endmacro

设置的过程如下图所示

e7745b36e57e2a3afafaf577978ba9ff.png

c365b96e67648bb3522e82498eb0da82.png

fbf65f2e49b99f488365805b1c3fafbf.png

b1e75a7d764cf0d3a88516afab708906.png

通过make之后得到的结果

9e8a5b185be0ba5c163d38c5962fb422.png

8d7401c219b8a0e9924ebfae13516a43.png

在这里的打印结果为字符串,并没有分页的显示效果,在后面的博客会对该次实验进行解释

小结

1.x86处理器直接支持内存分页机制

2.分页机制启动后,使用二级页表对内存进行管理

3.页目录和单个子页表占用1内存页

4.页面起始地址按4K字节对齐

5.分页后可访问的虚拟内存空间为4G

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值