NoteZ
NoteZ copied to clipboard
<x86汇编语言:从实模式到保护模式> NOTE
Prologue
读完了感觉这本书讲的还是很基础, 但是有写有些地方只是一点而过, 没有解释为啥. 只能作为入门第一个本资料.
实力推荐 <操作系统真相还原> 这本书, 讲的非常仔细. 虽然有点废话多.
Somniloquy
16.3.2 任务全局空间和局部空间的页面映射
在保护模式时, 段部件产生线性地址就是物理地址. 但是开启分页机制(即: cr3 指定页目录地址, cr0 指定分页 flag), 此时段部件产生的线性地址并不是物理地址, 需要通过页部件从页目录->目录项->页表->页表项->物理页地址.
以上前提, 由于 0x80000000~0xFFFFFFFF 为内核空间, 所以此时需要重新映射物理地址 0x0 到 线性地址0x80000000, 所以此时需要在分页机制状态下修改页目录中的 0x80000000 的目录项为之前在未开启分页机制下的保护模式中的目录项的物理地址.
此时问题在于如何找到页目录在分页机制下的线性地址, 原来在未开启分页机制的保护模式时, 已经把页目录当成页表放在索引位置0x3FF即最后一个目录项. 所以整个过程有些嵌套的意思. 所以可以认为页目录的地址在 页目录的索引为0x3FF的目录项对应的页表(此时页表就是页目录), 该页表下索引为0x3FF的物理页(此时物理页就是目录页).
所以此时页目录的在分页机制下的线性地址为 0xFFFFF000, 此时加上线性地址0x80000000对应的目录项的索引修改之前的物理地址即可.
16.4.3 创建页表并登记分配的页
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
这一段还是很重要的, 前提有个 page_bit_map
已保存可用的物理内存页.
当需要分配一个物理内存页时, 传入要分配的内存的线性地址. 所以需要先线性地址对应的高10位目录项对应的页表是否存在, 如果页表不存在, 需要先利用 allocate_a_4k_page
分配一个页表. 之后在该页表的基础上找到线性地址对应的中间10位页表项, 并设置 allocate_a_4k_page
得到的物理页.
16.5.6 复制页目录表
从逻辑上讲, 0x0~0x80000000
为用户任务, 0x80000000~0xFFFFFFFF
为内核空间. .在内核载入用户任务, 需要为用户任务初始化任务自己的目录页. 这里采用的做法是复制一份内核内的目录页给用户任务.
所以现在的解决方法就是把新的内核页的物理地址注册到内核页目录页的倒数第2个目录项(倒数第一个目录项是当前内核页).
所以问题来了, 内核目录页倒数第2个目录项对应的线性地址是多少? 首先我们知道, 当前目录页的线性地址是 0xFFFFF000, 所以当前内核目录页的线性地址加上偏移即为正确的线性地址, 也就是说, 0xFFFFF000 + (0x1000 - 0x8), 所以可以向这个线性地址写入新的目录页的物理地址.
那么之后如何访问新的目录页呢,, 也就是新的目录页的线性地址是多少? 也就是高 10 位为 0x3FF, 中间 10 位为 0x3FF, 也就是 0xFFFFE000.