以前我研究过ucosII的源代码,对应于x86来说,ucos是实模式下面的操作系统。而Minix是保护模式下的操作系统,自然他们的Context Switch有不一样的地方,现在,我就结合我知道的知识谈一下二者的区别。
Context Switch是多任务操作系统必不可少的部分,它的主要任务就是保护cpu的执行现场,将cpu的寄存器保存起来。在这一点上Minix3 和ucosII是没有区别的。同时二者在实现的层面上都和堆栈有关。
现在着重谈一下他们的区别:
第一个首要关注的地方就是x86的硬件体系,保护模式下的内容远远比实模式丰富,研究保护模式下的操作系统,必然跟GDT、LDT、IDT发生关系。所以,保护模式下Conext Switch就要比实模式复杂些。
ucosII在实现Context Switch时,仅仅将cpu的执行现场也就是寄存器保存在任务自己的堆栈中,保存和还原任务寄存器的功能都是通过中断机制完成的。保护模式下的Context Switch的内容就要丰富的多,首要提到的东西就是新增加的tr寄存器,tr寄存器保存的是任务状态段
TSS的段选择子。
struct tss_s


{
reg_t backlink;

reg_t sp0; /**//* stack pointer to use during interrupt */

reg_t ss0; /**//* " segment " " " " */
.
.
}
struct tss_s tss;
tss.ss0 = DS_SELECTOR;
init_dataseg(&gdt[TSS_INDEX], vir2phys(&tss), sizeof(tss), INTR_PRIVILEGE);
gdt[TSS_INDEX].access = PRESENT | (INTR_PRIVILEGE << DPL_SHIFT) | TSS_TYPE; 可以看出,TSS段实际上就是保存一个tss_s结构。那tss_s又有什么用?
首先需要说明,保护模式下发生Context Switch时,cpu寄存器并不是保存在自己的工作堆栈中的,而是保存在进程控制块中的。
struct proc


{

struct stackframe_s p_reg; /**//* process' registers saved in stack frame */
.
.
} tss_s的两个最重要的成员就是sp0和ss0。ss0用来指明任务控制块位于数据段内,sp0就指向p_reg的最高位,因为x86的堆栈是向下增加的。
这样,当中断发生时,通过tr就能够找到tss的sp0和ss0,然后就能够寻址到p_reg,中断将自动压入一些寄存器的指,另外一些则需要我们来压入,如下通过save函数。以下是一个简化的中断处理过程。
call save
.
ret !为什么是ret而不是iret,因为ret会跳到_restart然后iret(?push _restart?)

相关代码如下:

.align 16
save:
cld ! set direction flag to a known value
pushad ! save EAX, ECX, EDX, EBX, original ESP, EBP,
! ESI, and EDI registers
o16 push ds ! save ds
o16 push es ! save es
o16 push fs ! save fs
o16 push gs ! save gs
mov dx, ss ! ss is kernel data segment, tss.ss0 = DS_SELECTOR
mov ds, dx ! load rest of kernel segments
mov es, dx ! kernel does not use fs, gs
mov eax, esp ! prepare to return
incb (_k_reenter) ! from -1 if not reentering
jnz set_restart1 ! stack is already kernel stack
mov esp, k_stktop !
push _restart ! build return address for int handler
xor ebp, ebp ! for stacktrace
jmp RETADR-P_STACKBASE(eax) !堆栈sp已经被改变了,所以只能够通过硬寻址找到返回地址

.align 4
set_restart1:
push restart1 !存在中断嵌套
jmp RETADR-P_STACKBASE(eax)
_restart:

! 找到更高优先级的任务,并将相应任务控制块的p_reg赋值给tss.sp0

cmp (_next_ptr), 0 ! see if another process is scheduled
jz 0f
mov eax, (_next_ptr)
mov (_proc_ptr), eax ! schedule new process
mov (_next_ptr), 0
0: mov esp, (_proc_ptr) ! will assume P_STACKBASE == 0
lldt P_LDT_SEL(esp) ! enable process' segment descriptors
lea eax, P_STACKTOP(esp) ! arrange for next interrupt
mov (_tss+TSS3_S_SP0), eax ! to save state in process table
restart1:
decb (_k_reenter)
o16 pop gs
o16 pop fs
o16 pop es
o16 pop ds
popad
add esp, 4 ! skip return adr(?call save?)
iretd ! continue process

-----------------------------------------------------------------------------------------------------------
如果随笔中有什么错误,还请大家指教。谢谢!
posted on 2006-09-04 15:57 莫问春秋 阅读(4562)
评论(2) 编辑 收藏