当物理内存不够时就把不常用的内存暂时存入磁盘,并且描述符的P位置0,把要使用的段放入内存,描述符P位置1
但是这种方式会产生大量内存碎片,影响内存分配效率
设想一个虚拟内存,每隔任务都有他独立的虚拟内存,虚拟内存和实际物理内存大小相等
因为创建系统内核的页目录时,内核程序已经加载到内存了,为了方便起见,本书设定在内核的页部件输出的物理地址应与段部件输出的地址相同
页目录项和页表项的组成格式:
- P:存在位,为1时表示页表或页存在在内存中
- RW:读写位,为1时可读可写
- US:用户/管理位,为1时允许所有访问,0时只允许0、1、2特权级访问
- PWT:和高速缓存有关,间接决定改善页面访问效率
- PCD:决定是否采用高速缓存
- A:已访问位,显示使用频率
- D:脏位,表示次页表或页已写入数据
- PAT:固定为0
- G:全局位,表示是否为全局性质的,放入高速缓存,访问速度加快
;准备打开分页机制
;创建系统内核的页目录表PDT
;页目录表清零
mov ecx,1024 ;1024个目录项
mov ebx,0x00020000 ;页目录的物理地址
xor esi,esi
.b1:
mov dword [es:ebx+esi],0x00000000 ;页目录表项清零
add esi,4
loop .b1
;在页目录内创建指向页目录自己的目录项
mov dword [es:ebx+4092],0x00020003
;在页目录内创建与线性地址0x00000000对应的目录项
mov dword [es:ebx+0],0x00021003 ;写入目录项(页表的物理地址和属性)
;创建与表目录项相对应的页表,初始化页表项
mov ebx,0x00021000 ;页表的物理地址
xor eax,eax ;起始页的物理地址
xor esi,esi
.b2:
mov edx,eax
or edx,0x00000003
mov [es:ebx+esi*4],edx ;登记页的物理地址
add eax,0x1000 ;下一个相邻页的物理地址
inc esi
cmp esi,256 ;仅低端1MB内存对应的页才是有效的
jl .b2
.b3: ;其余的页表项置为无效
mov dword [es:ebx+esi*4],0x00000000
inc esi
cmp esi,1024
jl .b3
;令控制寄存器CR3指向页目录,并正式开启页功能
mov eax,0x00020000 ;PCD=PWT=0
mov cr3,eax
mov eax,cr0
or eax,0x80000000
mov cr0,eax ;在PE=1的情况下(保护模式)PG置1,开启分页机制
;在页目录内创建与线性地址0x80000000对应的目录项
mov ebx,0xfffff000 ;页目录自己的线性地址
mov esi,0x80000000 ;映射的起始地址
shr esi,22 ;线性地址的高10位是目录索引
shl esi,2
mov dword [es:ebx+esi],0x00021003 ;写入目录项(页表的物理地址和属性)
;目标单元的线性地址为0xFFFFF200
;将GDT中的段描述符映射到线性地址0x80000000
sgdt [pgdt]
mov ebx,[pgdt+2]
or dword [es:ebx+0x10+4],0x80000000 ;保护模式下初始代码段描述符
or dword [es:ebx+0x18+4],0x80000000 ;内核栈段描述符
or dword [es:ebx+0x20+4],0x80000000 ;显示缓冲区描述符
or dword [es:ebx+0x28+4],0x80000000 ;公共例程段描述符
or dword [es:ebx+0x30+4],0x80000000 ;内核数据段描述符
or dword [es:ebx+0x38+4],0x80000000 ;内核代码段描述符
add dword [pgdt+2],0x80000000 ;GDTR也用的是线性地址
lgdt [pgdt]
;为程序管理器的TSS分配内存空间
mov ebx,[core_next_laddr]
call sys_routine_seg_sel:alloc_inst_a_page
add dword [core_next_laddr],4096
alloc_inst_a_page: ;分配一个页,并安装在当前活动的
;层级分页结构中
;输入:EBX=页的线性地址
push eax
push ebx
push esi
push ds
mov eax,mem_0_4_gb_seg_sel
mov ds,eax
;检查该线性地址所对应的页表是否存在
mov esi,ebx
and esi,0xffc00000
shr esi,20 ;得到页目录索引,并乘以4
or esi,0xfffff000 ;页目录自身的线性地址+表内偏移
test dword [esi],0x00000001 ;P位是否为“1”。检查该线性地址是
jnz .b1 ;否已经有对应的页表
;创建该线性地址所对应的页表
call allocate_a_4k_page ;分配一个页做为页表
or eax,0x00000007
mov [esi],eax ;在页目录中登记该页表
.b1:
;开始访问该线性地址所对应的页表
mov esi,ebx
shr esi,10
and esi,0x003ff000 ;或者0xfffff000,因高10位是零
or esi,0xffc00000 ;得到该页表的线性地址
;得到该线性地址在页表内的对应条目(页表项)
and ebx,0x003ff000
shr ebx,10 ;相当于右移12位,再乘以4
or esi,ebx ;页表项的线性地址
call allocate_a_4k_page ;分配一个页,这才是要安装的页
or eax,0x00000007
mov [esi],eax
pop ds
pop esi
pop ebx
pop eax
retf
allocate_a_4k_page: ;分配一个4KB的页
;输入:无
;输出:EAX=页的物理地址
push ebx
push ecx
push edx
push ds
mov eax,core_data_seg_sel
mov ds,eax
xor eax,eax
.b1:
bts [page_bit_map],eax
jnc .b2
inc eax
cmp eax,page_map_len*8
jl .b1
mov ebx,message_3
call sys_routine_seg_sel:put_string
hlt ;没有可以分配的页,停机
.b2:
shl eax,12 ;乘以4096(0x1000)
pop ds
pop edx
pop ecx
pop ebx
ret