一、任务
任务是动态的概念,程序的执行状态。程序给的解释就是静态的,例如桌面上放了个五子棋的程序。
为了保存任务的状态,并在下次重新执行程序的时候恢复它们,每个任务都应当拥有一个额外的内存区域记录相关的信息,这叫做任务状态段(TSS)。
这个地方写这个与点不合适,因为么有完全看懂。
下面简单在串一下。
二、保护模式下的段式寻址
在前面简单介绍过,保护模式的寻址http://my.oschina.net/u/1185580/blog/182241
这里详细啰嗦下。
1.保护模式下的段是找的上图这种的描述符 每个描述符的大小是8个字节=2个双字
2.描述符有 全局描述符表、局部描述符秒、任务描述符表、中断、门;描述符都在全局描述符中定义
3.上图中的s=0 系统 type=1100(门);权限间的切换
0010(ldt 也就是局部描述符);
1001(任务ltr不忙);
1011(任务ltr忙)
=1 代码、数据段(type(第一位也就是11位)=0数据段;=1代码段)
下面是一个简单的描述符的定义
;创建1#描述符,这是一个数据段,对应0~4GB的线性地址空间
mov dword [ebx+0x08],0x0000ffff ;基地址为0,段界限为0xFFFFF
mov dword [ebx+0x0c],0x00cf9200 ;粒度为4KB,存储器段描述符
4.进入保护模式后,根据选择子得到 基地址+偏移量 然后检查权限、检查界限=安全的检查
5.暂且看的是,代码数据段立即加载过去了;
如果是局部描述表要先得到局部描述符的,lldt中,然后得到局部描述符表信息;(<64KB大小)
如果是任务描述符则就应该是后面的偏移量是没有用的
三、任务描述符的信息
这些是记录任务当前运行的状态
这里说明下调用门的格式:
31--------------------16-15----------------------------0
选择子 偏移量15-0
31--------------------16-15-14-13-12-11----8-7-4----0
偏移量 P |DPL| 0 |TYPE| 0 参数个数
调用门实施特权级的控制转移,可以使用jmp far指令,也可以使用 call far 指令
使用jmp far指令可以将控制通过门转移到比当前特权级别高的代码段,但不改变当前的特权级别;
如果使用call会改变当前特权级的CPL。1.堆栈的请求要求是必须与当前的特权级请求一致,哪意味着堆栈的切换
2.数据段的请求权限》=当前的权限
3.代码段的请求权限 同级别或者依从【(权限《=当前)才可以使用】
type 11-8 10位是C位
系统不允许高的权限转移到底的权限中。
call 段内转移 esp入栈
call 段间转移 cs、esp依次入栈
当没有特权级变化的时候,cal,jmp都可以进入任务门。
*这样的处理权限的转换问题
当特权级切换的时候哪?
LABEL_TSS:
DD 0 ; Back
DD 0 ; 0 级堆栈,现处于0级 esp的值
DD 0x00000020 ;堆栈选择子
DD 0 ; 1 级堆栈
DD 0 ;
DD 0 ; 2 级堆栈
DD 0 ;
DD 0 ; CR3
DD 0 ; EIP
DD 0 ; EFLAGS
DD 0 ; EAX
DD 0 ; ECX
DD 0 ; EDX
DD 0 ; EBX
DD 0 ; ESP
DD 0 ; EBP
DD 0 ; ESI
DD 0 ; EDI
DD 0 ; ES
DD 0 ; CS
DD 0 ; SS
DD 0 ; DS
DD 0 ; FS
DD 0 ; GS
DD 0 ; LDT 就是0
DW 0 ; 调试陷阱标志
DW $ - LABEL_TSS + 2 ; I/O位图基址 截取后16位
DB 0ffh ; I/O位图结束标志
TSSLen equ $ - LABEL_TSS
四、集中起来的一个小程序
;目的是分页的练习
;时间 2013-12-14 下午 周六
[org 0x7c00]
[bits 16]
ldt_description equ 0x00007F00
mov ax,cs
mov ds,ax
call show_style ;设置显示模式 主要是清屏
;计算GDT所在的逻辑段地址
mov eax,[pgdt+0x02] ;得到描述符的基地址
xor edx,edx
mov ebx,16
div ebx
mov ds,eax ;令DS指向该段以进行操作
mov ebx,edx ;段内起始偏移地址
;创建0#描述符,它是空描述符,这是处理器的要求
;创建1#描述符,这是一个数据段,对应0~4GB的线性地址空间
mov dword [ebx+0x08],0x0000ffff ;基地址为0,段界限为0xFFFFF
mov dword [ebx+0x0c],0x00cf9200 ;粒度为4KB,存储器段描述符
;创建保护模式下初始代码段描述符
mov dword [ebx+0x10],0x7c0001ff ;基地址为0x00007c00,界限0x1FF
mov dword [ebx+0x14],0x00409800 ;粒度为1个字节,代码段描述符
;建立保护模式下的堆栈段描述符 ;基地址为0x00007C00,界限0xFFFFE
mov dword [ebx+0x18],0x7c00fffe ;粒度为4KB
mov dword [ebx+0x1c],0x00cf9600
;建立保护模式下的显示缓冲区描述符
mov dword [ebx+0x20],0x80007fff ;基地址为0x000B8000,界限0x07FFF
mov dword [ebx+0x24],0x0040920b ;粒度为字节
;为了测试句不断选择子,大小就8个字节也就是一个ldt
mov eax,ldt_description
shl eax,16
or eax,0x00000007
mov dword [ebx+0x28],eax ;基地址为0,段界限为8
mov dword [ebx+0x2c],0x00408200 ;粒度为4KB,存储器段描述符
mov eax, LABEL_TSS ;TSS段基地址
shl eax,16
or eax,TSSLen-1
mov dword [ebx+0x28],eax
mov dword [ebx+0x2c],0x00408900
;=========================================================
;初始化描述符表寄存器GDTR
;因为上面吧数据段地址改了所以这利用代码段
mov word [cs:pgdt],55 ;描述符表的界限(总字节数减一) n*8-1;
lgdt [cs:pgdt]
in al,0x92 ;南桥芯片内的端口
or al,0000_0010B
out 0x92,al ;打开A20
cli ;保护模式下中断机制尚未建立,应
;禁止中断
mov eax,cr0
or eax,1
mov cr0,eax ;设置PE位
;以下进入保护模式... ...
jmp dword 0x0010:protect_loader-0x7C00;16位的描述符选择子:32位偏移
;hlt ;程序终止
;===================showStyle=============================
show_style: ;设置显示方式
mov ah,0x00
mov al,0x03
int 10h
ret
;=========================================================
[bits 32]
protect_loader:
xor eax,eax
mov eax,0000_0000_00100_000B ;#4 显示段
mov es,eax
mov byte[es:0x00],'H'
mov byte[es:0x02],'E'
mov byte[es:0x04],'L'
mov byte[es:0x06],'L'
mov byte[es:0x08],'O'
mov byte[es:0x0a],','
mov byte[es:0x0c],'O'
mov byte[es:0x0e],'S'
mov eax,0000_0000_00001_000B ;#1数据段 4G
mov es,eax
xor ebx,ebx
mov ebx,ldt_description; 得到偏移地址
mov dword [es:ebx+0x00],0x7c0001ff
mov dword [es:ebx+0x04],0x00409800 ;拼凑一个lldt的选择子
xor eax,eax
mov ax,0000_0000_00101_000B ;第五个描述符的位置
lldt ax ;加载lldt
;hlt
;jmp dword 0x0004:ldt_loader-0x7c00
mov eax,0000_0000_00110_000B ;#6 TSS段
ltr ax
xor esp,esp ;esp设置为0
jmp dword ax:0
hlt ;程序终止
;----------------------------------------------------------------
ldt_loader: ;这里是局部全局变量的加载
xor eax,eax
mov eax,0000_0000_00100_000B ;#4 显示段
mov es,eax
mov byte[es:0x00+0xa0],'L'
mov byte[es:0x02+0xa0],'L'
mov byte[es:0x04+0xa0],'D'
mov byte[es:0x06+0xa0],'T'
hlt;
LABEL_TSS:
DD 0 ; Back
DD 0 ; 0 级堆栈,现处于0级 esp的值
DD 0x00000020 ;堆栈选择子
DD 0 ; 1 级堆栈
DD 0 ;
DD 0 ; 2 级堆栈
DD 0 ;
DD 0 ; CR3
DD 0 ; EIP
DD 0 ; EFLAGS
DD 0 ; EAX
DD 0 ; ECX
DD 0 ; EDX
DD 0 ; EBX
DD 0 ; ESP
DD 0 ; EBP
DD 0 ; ESI
DD 0 ; EDI
DD 0 ; ES
DD 0 ; CS
DD 0 ; SS
DD 0 ; DS
DD 0 ; FS
DD 0 ; GS
DD 0 ; LDT 就是0
DW 0 ; 调试陷阱标志
DW $ - LABEL_TSS + 2 ; I/O位图基址 截取后16位
DB 0ffh ; I/O位图结束标志
TSSLen equ $ - LABEL_TSS
;--------------数据段--------------------------------------------
pgdt dw 0
dd 0x00007e00 ;GDT的物理地?
times 510-($-$$) db 0
db 0x55 ;引导识别标示
db 0xaa
上面 首先实例化了 1.全局段描述符表GDT;lgdt
2.然后跳转进入了保护模式
3.实例化了LDT的描述符表 通过4G的数据段
4.加载lldt、加载了ltr
5.最后根据ltr的这个选择子进入了,lldt中的选择子 0 (当然也可以直接跳转加载lldt的选择子)
效果如下