x01.os.9: 进程切换

时间:2023-03-08 22:12:49

进入内核后,当然不能无所事事。先创建三个进程,分别打印 A,B,C。虽然只是简单的打印,但却是一切扩展的基础,不可等闲视之。

进程切换,涉及一系列的寄存器需要保护,于是,就有了 ProcessStack 结构,代码如下:

typedef struct {
u32 gs;
u32 fs;
u32 es;
u32 ds;
u32 edi;
u32 esi;
u32 ebp;
u32 KernelEsp;
u32 ebx;
u32 edx;
u32 ecx;
u32 eax;
u32 RetAddr;
u32 eip;
u32 cs;
u32 eflags;
u32 esp;
u32 ss;
} ProcessStack;

稍加注意,会发现有个 KernelEsp。它的作用,是防止进程切换时,栈指针乱指。

进程切换,当然离不开中断。有意思的是,涉及中断调用的任务状态栈 TSS 同 ProcessStack 竟有异曲同工之妙。

进程切换,还有一个关键,就是不能再用简单的 ret 来返回了。kernel.s 中的 save 代码如下:

save:
pushad
push ds
push es
push fs
push gs mov dx, ss
mov ds, dx
mov es, dx mov esi, esp
inc dword [g_IntReenter]
cmp dword [g_IntReenter],
jne .
mov esp, StackTop
push Restart
jmp [esi + P_RetAddr - P_StackBase]
.:
push reenter
jmp [esi + P_RetAddr - P_StackBase] Restart:
mov esp, [g_pProcReady]
lldt [esp + P_LdtSel]
lea esi, [esp + P_StackTop]
mov dword [g_Tss + TSS_ESP0], esi
reenter:
dec dword [g_IntReenter] pop gs
pop fs
pop es
pop ds
popad
add esp,
iretd

其中的 jmp  [esi + P_RetAddr - P_StackBase] ,就是跳到事先保存的返回地址。而这一返回地址,由 main.c 中的 KernelMain 设置。即 for 循环里的 pProc->Regs.esp = (u32)pTaskStack; 这种多兵种作战,需细心体会,方能领会之。

此关键点如能领会,进程切换就不是难事。所谓进程,不过在 ProcessStack 的基础上,添加一些进程 Id,name,优先级而已。

进入工程目录,make 后,再 bochs,即可看到如下界面:

x01.os.9: 进程切换

其中,优先级的设置为 15:5:3, 同显示的基本一致。完整代码,可到 x01.Lab.Download 中下载。虚拟机及开发工具,可参看 x01.os.7

有个问题需说明一下。就是修改后重新 make 时,会出现 /mnt/temp 忙,可运行命令 sudo umount /mnt/temp 卸载之。