进程switch
现在让我们考虑一个问题,如何确定当前Active的进程是哪个呢?要知道,进程并不独享某段代码,
我们不可能通过当前执行的代码来确定哪个进程处于Active状态。
显然,应该通过进程私有的东西来确定——而进程所私有的只有一样,即Kernel page 6(即ppda所在的Page)。
我们通过KISA6来确定Active进程,即KISA6指向哪个进程的“私有空间”,则该进程就是Active进程。
1 保存与切换
显然,要切换Active process,首先一件事就是保存当前process的若干信息,比如pc等等。而保存信息是由
savu来实现的。savu是我们的老朋友了,它的代码如下:
0725 _savu:
0726 bis $340,PS
0727 mov (sp)+,r1 /Return Address
0728 mov (sp),r0 /参数(u.u_rsav) --->r0
0729 mov sp,(r0)+ /sp --->u.ursav[0]
0730 mov r5,(r0)+ /r5 --->u.ursav[1]
0731 bic $340,PS
0732 jmp (r1) /return
调用例子如下:
2189 savu(u.u_rsav);
一般情况下,它的参数总是u.u_rsav,也即u的开始地址(也是进程私有空间的开始地址),调用之后,
会将当前sp和r5保存在u中。savu并没有保存pc——这并不奇怪,因为,在每次函数调用中,栈中都会
存放return address。可以通过它来恢复pc。
而进程的切换是由retu来完成的,retu的调用一般是这样的:
2228 retu(rp->p_addr); 参数为要被Active的进程的“私有空间”起始block号(即kisa6 page)
0740 _retu:
0741 bis $340,PS
0742 mov (sp)+,r1 /return adderss ---->r1
0743 mov (sp),KISA6 /参数--->KISA6,切换了PPDA,其实也就切换了Process
0744 mov $_u,r0 /即u.u_rsav
0745 1:
0746 mov (r0)+,sp /恢复sp
0747 mov (r0)+,r5 /恢复r5
0748 bic $340,PS
0749 jmp (r1)
2 Return到哪里去?
retu切换了进程,同时也恢复了sp和r5,但并没有更新pc,即没有跳回到新进程应该执行的地方。
pc的跳转是在retu的调用者函数中完成的,让我们看一个例子:
2178 swtch()
……
2228 retu(rp->p_addr);
……
2247 return(1);
2248 }
由于切换了sp,swtch在return时,不会回到调用swtch的地方。那末,它会跳回到哪里去呢?也许你会说,
跳回到“原来”调用savu保存rp进程的那个函数——很不幸,这个说法是错误的。
让我们仔细研究下savu的代码:
0725 _savu:
0726 bis $340,PS
0727 mov (sp)+,r1 /Return Address
0728 mov (sp),r0 /参数(u.u_rsav) --->r0
在保存sp之前,第727行改变了sp,弹出了Return Address——这一作法,事实上抹平了因savu调用而对栈结构
的影响,使sp中记录的Return Address成为了savu调用者函数在被调用时,写入的return Address。即,如果当初
savu的调用链如下:
调用 调用
函数1------>函数2----->savu
则return时,会return到函数1。
神乎其技!
【思考题】:第728行是否有印刷错误?是否应该为mov (sp)+,r0——这样,栈顶才是你刚刚所说的
“那个Return Address”
提示:请注意以下两个事实:
(1) 栈的清理是由调用者完成的;
(2) savu和retu具有相同的参数数量。
博客地址:http://blog.csdn.net/cszhao1980
博客专栏地址:http://blog.csdn.net/column/details/lions-unix.html