实验4-基于内核栈切换的进程切换
一、实验内容
1.schedule 与 switch_to
目前 Linux 0.11 中工作的schedule()
函数是首先找到下一个进程的数组位置 next
,而这个next
就是 GDT
中的 n
,所以这个 next
是用来找到切换后目标 TSS
段的段描述符的,一旦获得了这个 next
值,直接调用上面剖析的那个宏展开 switch_to(next);
就能完成 TSS
切换所示的切换了。
现在,我们不用 TSS
进行切换,而是采用切换内核栈的方式来完成进程切换,所以在新的 switch_to
中将用到当前进程的PCB
、目标进程的 PCB
、当前进程的内核栈、目标进程的内核栈等信息。
因此需要将目前的 schedule()
函数(在 kernal/sched.c 中)做稍许修改,即将下面的代码:
if ((*p)->state == TASK_RUNNING && (*p)->counter > c)
c = (*p)->counter, next = i; /* i = NR_TASKS ,NR_TASK应该是定义在其他头文件的一个宏?*/
//......
switch_to(next);
修改为:
if ((*p)->state == TASK_RUNNING && (*p)->counter > c)
c = (*p)->counter, next = i, pnext = *p;
//.......
switch_to(pnext, _LDT(next));
这样,pnext
就指向下个进程的PCB
。
在schedule()
函数中,当调用函数switch_to(pnext, _LDT(next))
时,会依次将参数2 _LDT(next)
、参数1 pnext
、返回地址 }
压栈。
当执行switch_to
的返回指令ret
时,就回弹出schedule()
函数的}
执行schedule()
函数的返回指令}
。
2.编写switch_to汇编代码
为了将linux-0.11基于TSS
切换内核线程的方式修改成基于PCB
的方式,需要将原来放在 (/oslab/linux-0.11/include/linux/sched.h) 的switch_to
注释掉,转而直接在 (/oslab/linux-0.11/kernel/system_call.s) 中添加由汇编代码编写的新的switch_to
代码
switch_to:
pushl %ebp
movl %esp,%ebp
pushl %ecx
pushl %ebx
pushl %eax
movl 8(%ebp),%ebx
cmpl %ebx,current
je 1f
#切换PCB
movl %ebx,%eax
xchgl %eax,current
#重写TSS指针
movl tss,%ecx
addl $4096,%ebx
movl %ebx,ESP0(%ecx)
#切换内核栈
movl %esp,KERNEL_STACK(%eax