27 x86 系统上的内存分页

 

参考

https://blog.51cto.com/13475106/category6.html及狄泰软件相关课程

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

1.硬件层直接支持内存分页机制
2.默认情况下不使用分页机制(段式内存管理)
3.分页机制(进行配置)启动后,使用二级页表对内存进行管理
x86系列处理器的分页方式(32位)
操作系统-X86系统上的内存分页
如图所示,32位被分成了三部分
1.在低12位中,表示的是页内偏移地址
2.在中间10位,用于在子页表中查找目标页地址
3.在最高的10位中,用于子页目录中查找页表地址

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

操作系统-X86系统上的内存分页

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简单的分页构建方式
操作系统-X86系统上的内存分页
可以通过for循环构建目录,子页表,主要原因是一个一个的生成的

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

操作系统-X86系统上的内存分页
1.由于物理页面的地址必须按照4K字节对齐
2.由此可得,页目录可使用地址的低12位进行属性描述
在x86系列处理器上查看页属性的说明
操作系统-X86系统上的内存分页

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

如下所示
1.将页目录起始地址放置到cr3-该寄存器类似指针指向页目录起始地址
2.将cr0里面的值取出放置到eax寄存器中,
3.将cr0里面的值所对应二进制最高位置1--硬件级开启分页机制
操作系统-X86系统上的内存分页

在这里需要注意的是

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

1
2
3
4
5
6
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)

1
2
3
4
5
mov es,ax
mov edi,
mov eax,0XFF
cld
stosd

编程实现
loader.asm与inc.asm的设置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
%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

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
; 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

设置的过程如下图所示
操作系统-X86系统上的内存分页操作系统-X86系统上的内存分页操作系统-X86系统上的内存分页操作系统-X86系统上的内存分页
通过make之后得到的结果
操作系统-X86系统上的内存分页
操作系统-X86系统上的内存分页
在这里的打印结果为字符串,并没有分页的显示效果,在后面的博客会对该次实验进行解释 

 

小结

1.x86处理器直接支持内存分页机制
2.分页机制启动后,使用二级页表对内存进行管理
3.页目录和单个子页表占用1内存页
4.页面起始地址按4K字节对齐
5.分页后可访问的虚拟内存空间为4G

 

  

  

  

posted on   lh03061238  阅读(206)  评论(0编辑  收藏  举报

编辑推荐:
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)

导航

< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5
点击右上角即可分享
微信分享提示