学习笔记
《x86汇编语言:从实模式到保护模式》
https://www.jianshu.com/p/d481cb547e9f
详细调用关系以及过程在整个内核程序中的作用
参见 https://www.jianshu.com/p/2dba8674e8fd
内核程序:过程[alloc_inst_a_page]
代码流程
- 1、根据传入的线性地址EBX,计算对应的页目录表表项的线性地址,存入ESI;
- 2、视页表存在与否,尚未存在则,创建页表,登记页表;
- 3、根据传入的线性地址EBX,计算对应的页表的线性地址,存入ESI;
- 4、根据传入的线性地址EBX + 页表的线性地址,计算页表表项的线性地址,存入ESI;
- 5、为用户程序,申请一个空闲的物理页,物理页的物理地址通过EAX传回,配置页属性,将EAX存入页表([ESI]);
取自源码文件 c16_core.asm
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 ;页目录自身的线性地址+表内偏移
;ESI = 页目录表表项的线性地址
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 ;得到该页表的线性地址
;ESI=页表的线性地址
;得到该线性地址在页表内的对应条目(页表项)
and ebx,0x003ff000
shr ebx,10 ;相当于右移12位,再乘以4
or esi,ebx ;页表项的线性地址
;ESI=页表表项的线性地址
call allocate_a_4k_page ;分配一个页,这才是要安装的页
or eax,0x00000007
mov [esi],eax
pop ds
pop esi
pop ebx
pop eax
retf
注意:下列图解中可能的存在一个容易混淆的部分
- 有关移位操作的代码,比如右移12位再左移2位,往往直接写成右移动10位,本质上,左移两2位就是乘以4;
- 处理器进行地址转换时,对于线性地址是按照高10位 中10位 低12位的格式去解读的,要做的运算是
页目录表物理地址 + 高10位x4 = 页目录表项的线性地址
页表物理地址 + 中10位x4 = 页表表项的线性地址
物理页物理地址 + 低12位 = 真正的物理地址
- 可知,低12位本来是不需要乘以4的,但是下面的代码要么是访问页目录的表项,要么是访问页表的表项,这里面暗含着一个逻辑,那就是新的低12位其实是从旧的线性地址的要么高10位、要么中10位通过移位操作而来的,并且需要与页目录表的物理地址或者页表的物理地址结合,在移位的过程中往往就隐藏地进行了X4的操作,表现在代码的汇编指令上就是
左移2位
,然后又由于汇编语法的规则,先右移22位再左移2位
等价于直接右移20位
,表现在图解里就是,结果的线性地址里,最右端的两位的两个0永远都不是来自最初的线性地址,而是过程中的补0,我往往使用空白底色标识出来了;
一、检查该线性地址所对应的页表是否存在
mov esi,ebx
and esi,0xffc00000
shr esi,20 ;得到页目录索引,并乘以4
or esi,0xfffff000 ;页目录自身的线性地址+表内偏移
test dword [esi],0x00000001 ;P位是否为“1”。检查该线性地址是
jnz .b1 ;否已经有对应的页表
1、传入的参数 EBX=页的线性地址
,有何独特之处?
- 物理页的大小是
4KB
,即0x1000 Byte
; - 第一个可以分配的物理页的线性起始基地址是
EBX = 0x 0000 0000
; - 那么,第二个可以分配的物理页的线性起始基地址就是
EBX = 0x0000 0000 +0x1000 = 0x 0000 1000
; - 同理,第三个可以分配的物理页的线性起始基地址就是
EBX = 0x0000 1000+ 0x1000 = 0x 0000 2000
; - 可以很清楚地看见,作为传入参数的线性起始基地址,一定是
EBX = 0x ?????000
,低12位必然全是0
;
2、图解假设传入参数为 EBX =0x11122000
进行移位操作
假设的EBX
要满足作为物理页的线性起始基地址的基本要求,即低12位全是0,表现在十六进制0x
的写法上就是0x?????000
;and 指令
是为了保留,为了保留传入的线性地址的高10位,以后叫原来的高10位;or 指令
是为了强制置为1,就是要把新的高10位以及新的中10位霸道地全部设置为1,因为0xFFFFF???
是页目录表自己的线性基地址;这一小段代码, 就是为了计算出一个,可以访问页目录表自己表项的线性地址;
如果访问页目录表自己的表项,具体原理
参见 https://www.jianshu.com/p/d6b534560669
这里只需要记住,一个线性地址,只要是以
0xFFFFF...
开头的那么一定是访问页目录表自己;那么,原来的高10位到哪里去了?到低12位去了。现在,我们要访问的是页目录表自己的表项,这等于就是说,现在我们要访问一个物理页,页目录表就是这个物理页;
高10位 要乘以4 作为页目录表偏移量
低12位 直接作为 页的偏移量
原来的高10位变成了 现在的低12位
页目录表 不仅做页目录表 还做页表 现在更要作为 页
现在的低12位 与 现在的页 正好结合
- 现在,
ESI = 可以访问 页目录表自己表项的线性地址
;
3、从页目录表表项中取出页表的物理地址,查看对应的页表是否已经存在
test dword [esi],0x00000001
jnz .b1
-
P = 1
表示存在,则跳转到.b1
去继续执行; -
P= 0
表示还不存在,需要先申请一个物理页当做页表,然后再继续执行;
二、为页表申请一个物理页
;创建该线性地址所对应的页表
call allocate_a_4k_page ;分配一个页做为页表
or eax,0x00000007
mov [esi],eax ;在页目录中登记该页表
- 调用过程 allocate_a_4k_page
参见 https://www.jianshu.com/p/49cbaccd38c5
- 传回
EAX = 分配的空闲物理页的物理地址
- 配置页的属性
or eax,0x00000007
:US=1,RW=1,P=1
; - 完成登记
mov [esi],eax ;在页目录中登记该页表
三、计算要访问的页表的线性地址
.b1:
;开始访问该线性地址所对应的页表
mov esi,ebx
shr esi,10
and esi,0x003ff000 ;或者0xfffff000,因高10位是零
or esi,0xffc00000 ;得到该页表的线性地址
- 可见,要访问页表,线性地址必然是以
0xFFC.....
开头; - 访问页表表项,具体原理
参见 https://www.jianshu.com/p/d6b534560669
- 现在,
ESI = 页表的线性地址
四、访问页表表项
;得到该线性地址在页表内的对应条目(页表项)
and ebx,0x003ff000
shr ebx,10 ;相当于右移12位,再乘以4
or esi,ebx ;页表项的线性地址
call allocate_a_4k_page ;分配一个页,这才是要安装的页
or eax,0x00000007
mov [esi],eax
- 步骤三,
ESI = 页表的线性地址
- 过程 call allocate_a_4k_page 申请了一个物理页作为安装用户程序的物理页
EAX = 分配的空闲的物理页的物理地址
- 配置页属性之后,填入页表表项
mov [esi],eax