_restart:
! Restart the current process or the nextprocess if it is set.
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
iretd ! continue process
1) proc_ptr、next_ptr指针
EXTERN struct proc *proc_ptr; /* pointer to currently running process */
EXTERN struct proc *next_ptr; /* next process to run after restart() */
next_ptr为挑选出来的待执行的进程在进程表中对应表项的首地址。
2) lldt P_LDT_SEL(esp)
P_LDT_SEL是进程表项中第一个字段栈帧后面的第二个字段局部描述符表的段描述符在全局描述符表中对应的选择符,该指令将待执行进程的LDT段的段选择符加载到LDTR寄存器中。之后通过GDTR和LDTR便能够获取到该进程的代码段和数据段在内存中的首地址、长度、访问权限和粒度等信息。
3) lea eax, P_STACKTOP(esp) /mov (_tss+TSS3_S_SP0),eax
将待执行进程对应的进程表项中的栈帧字段的高地址赋值到tss的sp0字段,用途是stackpointer to use during interrupt,此外tss的ss0字段在main函数中设置为内核数据段的段选择符,用途是stack segment to use during interrupt。
4) o16 pop gs(fs/es/ds)
当前esp指向待执行进程的栈帧低地址,pop指令地址由低地址向高地址,依次将栈帧中的gs,fs,es,ds分别出栈。
5) popad
IA-32的popad指令在堆栈中按顺序弹出寄存器的值:
edi,esi,ebp,esp,ebx,edx,ecx,eax.(顺序与栈帧一致)
另IA-32的pushad指令在堆栈中按顺序压入下列寄存器:
eax,ecx,edx,ebx,esp,ebp,esi和edi.
popad指令pushad指令互逆。
6) add esp, 4 ! skip return adr/
ESP跳过一个retaddr。
7) iretd ! continue process
最后一步IRET指令,将栈顶五个值弹出并分别存eip,cs,elfag,esp,ss,完成程序最后的恢复,这一操作由硬件完成。此时出现奇怪的情况:esp的值明明已经被设置成进程被中断时的值了,但随后又正常恢复了ss,所以cpu里肯定有某处看不见的寄存器先保存了ss和esp的内容