任务转换
80386不仅支持多种工作模式,而且还支持多任务。所谓多任务,是指系统在同一时间内能够运行多个任务。但我们知道,CPU在每一个确定的时刻只能执行一个指令流,要实现多任务,只能不断地在多个任务之间做切换。现在来看看如何进行任务切换。
要进行任务切换,就要用到任务状态段TSS。任务状态段保存着各个任务的各个寄存器的值(通用寄存器、堆栈指针、标志寄存器等)、操作系统的有关信息、任务所访问的I/O口等。
任务切换有两种方法,可以直接通过任务状态段TSS切换,也可以通过任务门进行切换。直接通过TSS切换时,用CALL指令指向一个任状态段,执行CALL指令,则CPU会保存当前的任务状态,并取出目标任务状态段的状态装入CPU,执行新的任务状态的任务,这样就完成了一次任务转换。
通过任务门进行切换时,将任务门的选择子指向某个任务的TSS描述符,用CALL指令指向任务门,执行CALL指令,则CPU就会进行任务切换,并且交换任务状态。
一般来说,分时操作系统会在定时器中断时判断是否要进行任务切换,操作系统查看当前进程或线程的时间片是否已用完,若是,则进行任务切换。
例5.5 编写程序,用任务状态段进行任务切换。
.386p
JUMP16 MACRO selector,offsetv ;无条件转移宏指令
DB 0EAH
DW offsetv
DW selector
ENDM
;------------------------------------------
CALL16 MACRO selector ,offsetv ;过程调用宏指令
DB 09AH
DW offsetv
DW selector
ENDM
;------------------------------------------
Descriptor STRUC ;描述符结构体
limitl dw 0
basel dw 0
basem db 0
attributes dw 0
baseh db 0
Descriptor ENDS
;===============================
Data Segment use16 ;数据段
gdt0 Descriptor <>
DataSel = $-gdt0
DataDes Descriptor <0ffffh,,,92H,>
CodeSel = $-gdt0
CodeDes Descriptor <0ffffh,,,98H,>
Tss1Sel = $-gdt0 ;Tss1 Descriptor
Tss1Des Descriptor <Tss1Len-1,,,89H,>
Tss2Sel = $-gdt0 ;Tss2 Descriptor
Tss2Des Descriptor <Tss2Len-1,,,89H,>
Stack1Sel = $-gdt0 ;任务1堆栈段选择子
Stack1Des Descriptor <0,,,97H,> ;任务1堆栈段描述符
Stack2Sel = $-gdt0 3
Stack2Des Descriptor <0,,,0F7H,>
Tss2CodeSel = $-gdt0 3 ;任务2代码段选择子
Tss2CodeDes Descriptor <0ffffh,,,0F8H,> ;任务2代码段描述符
GdtLen = $-gdt0
GdtPtr dw GdtLen-1
dd 0
;
Tss1 label byte ;任务1的任务状态段
dd 0
dd 0 ;Stack Pointer for Ring 0
dw 0,0
dd 0 ;Stack Pointer for Ring 1
dw 0,0
dd 0 ;Stack Pointer for Ring 2
dw 0,0
dd 0 ;cr3
dd 0 ;eip
dd 0 ;eflags
dd ? ;eax
dd ? ;ecx
dd ? ;edx
dd ? ;ebx
dd ? ;esp
dd ? ;ebp
dd ? ;esi
dd ? ;edi
dw 0,0 ;es
dw 0,0 ;cs
dw 0,0 ;ss
dw 0,0 ;ds
dw 0,0 ;fs
dw 0,0 ;gs
dw 0,0 ;ldt
dw 0
dw $ 2
db 0ffh
Tss1Len = $-Tss1
Tss2 label byte ;任务2的任务状态段
dd 0 ;Link
dd 0 ;Stack Pointer for Ring 0
dw 0,0
dd 0 ;Stack Pointer for Ring 1
dw 0,0
dd 0 ;Stack Pointer for Ring 2
dw 0,0
dd 0 ;cr3
dw offset Tss2Begin,0 ;eip
dd 0 ;eflags
dd ? ;eax
dd ? ;ecx
dd ? ;edx
dd ? ;ebx
dd 1024 ;esp
dd ? ;ebp
dd ? ;esi
dd ? ;edi
dw 0,0 ;es
dw Tss2CodeSel,0 ;cs
dw Stack2Sel,0 ;ss
dw 0,0 ;ds
dw 0,0 ;fs
dw 0,0 ;gs
dw 0,0 ;ldt
dw 0
dw $ 2
db 0ffh
Tss2Len = $-Tss2
;
OldSPSS dw 0,0
Data ends
;==============================
Stack1 Segment use16 ;任务1的堆栈段
db 1024 dup (0)
Stack1 Ends
Stack2 Segment use16 ;任务2的堆栈段
db 1024 dup (0)
Stack2 Ends
;==============================
Tss2Code Segment use16 ;任务2的代码段
assume cs:Tss2Code
Tss2Begin:
iretd
Tss2Code Ends
;=============================
Code Segment use16 ;代码段
assume cs:Code,ds:Data
Start:
mov ax,Data
mov ds,ax
xor eax,eax ;初始化数据段描述符
mov ax,Data
shl eax,4
mov dword ptr [GdtPtr 2],eax ;GDT地址
xor eax,eax ;初始化代码段描述符
mov ax,Code
shl eax,4
mov CodeDes.basel,ax
shr eax,16
mov CodeDes.basem,al
mov CodeDes.baseh,ah
mov ax,Data ;Tss1,初始化任务1的状态段描述符
movzx eax,ax
shl eax,4
xor ebx,ebx
mov bx,offset Tss1
add eax,ebx
mov Tss1Des.basel,ax
shr eax,16
mov Tss1Des.basem,al
mov Tss1Des.baseh,ah
mov ax,Data ;Tss2,初始化任务2的状态段描述符
movzx eax,ax
shl eax,4
xor ebx,ebx
mov bx,offset Tss2
add eax,ebx
mov Tss2Des.basel,ax
shr eax,16
mov Tss2Des.basem,al
mov Tss2Des.baseh,ah
mov ax,Stack1 ;Stack
movzx eax,ax
shl eax,4
mov Stack1Des.basel,ax
shr eax,16
mov Stack1Des.basem,al
mov Stack1Des.baseh,ah
mov ax,Stack2
movzx eax,ax
shl eax,4
mov Stack2Des.basel,ax
shr eax,16
mov Stack2Des.basem,al
mov Stack2Des.baseh,ah
mov ax,Tss2Code ;Tss2Code
movzx eax,ax
shl eax,4
mov Tss2CodeDes.basel,ax
shr eax,16
mov Tss2CodeDes.basem,al
mov Tss2CodeDes.baseh,ah
lgdt fword ptr GdtPtr ;Load GDT
cli
mov OldSPSS,sp
mov [OldSPSS 2],ss
mov eax,cr0 ;转到保护模式
or eax,1
mov cr0,eax
JUMP16 CodeSEL,<offset Protect>
Protect:
mov ax,DataSel
mov ds,ax
mov es,ax
mov fs,ax
mov gs,ax
mov ax,Stack1Sel
mov ss,ax
mov sp,1024
mov ax,Tss1Sel
ltr ax ;装入任务状态寄存器
CALL16 Tss2Sel,0 ;任务切换
mov eax,cr0
and eax,0fffffffeH
mov cr0,eax
JUMP16 Code,<offset Real>
Real:
mov ax,data
mov ds,ax
lss sp,dword ptr OldSPSS
sti
mov ax,4C00H
int 21H
Code ends
end Start