By cszhao1980
本章将探讨unix v6代码中最微妙的部分,即著名的注释:“ You are not expected to understand this”。
2178: swtch()
2179: {
2180: static struct proc *p;
2181: register i, n;
2182: register struct proc *rp;
……
2228: retu(rp->p_addr);
2229: sureg();
2230: /*
2231: * If the new process paused because it was
2232: * swapped out, set the stack level to the last call
2233: * to savu(u_ssav). This means that the return
2234: * which is executed immediately after the call to aretu
2235: * actually returns from the last routine which did
2236: * the savu.
2237: *
2238: * You are not expected to understand this.
2239: */
2240: if(rp->p_flag&SSWAP) {
2241: rp->p_flag =& ~SSWAP;
2242: aretu(u.u_ssav);
2243: }
2244: /* The value returned here has many subtle implications.
2245: * See the newproc comments.
2246: */
2247: return(1);
2248: }
相关代码仅有三行,逻辑非常简单:
(1) 在swtch()退出之前,会检查进程的SSWAP标志;
(2) 如该标志置位,则清理该标志,然后调用aretu(u.u_ssav)。
首先看一下aretu(),这个函数与retu()的作用类似:
(1) 使用参数指向的内容更新sp和r5;
(2) 不同的是,aretu()不会更新KISA6,即不会进行进程切换。
看起来,swtch()神秘代码的作用是调整sp和r5,以使其在return时跳到另外的地
方——即savu(u.u_ssav);的调用者的调用者。
接下来,让我们看看,会设置SSWAP标志的几种情况:
(1) expand函数;
2268: expand(newsize)
2269: {
2270: int i, n;
……
2281: savu(u.u_rsav);
2282: a2 = malloc(coremap, newsize);
2283: if(a2 == NULL) {
2284: savu(u.u_ssav);
2285: xswap(p, 1, n);
2286: p->p_flag =| SSWAP;
2287: swtch();
2288: /* no return */
2289: }
……
2296: }
(2) xalloc函数;
4433: xalloc(ip)
4434: int *ip;
4435: {
……
4474: out:
4475: if(xp->x_ccount == 0) {
4476: savu(u.u_rsav);
4477: savu(u.u_ssav);
4478: xswap(u.u_procp, 1, 0);
4479: u.u_procp->p_flag =| SSWAP;
4480: swtch();
4481: /* no return */
4482: }
4483: xp->x_ccount++;
4484: }
(3) newproc函数;
1826: newproc()
1827: {
1828: int a1, a2;
……
1896: a2 = malloc(coremap, n);
1897: /*
1898: * If there is not enough core for the
1899: * new process, swap out the current process to generate the
1900: * copy.
1901: */
1902: if(a2 == NULL) {
1903: rip->p_stat = SIDL;
1904: rpp->p_addr = a1;
1905: savu(u.u_ssav);
1906: xswap(rpp, 0, 0);
1907: rpp->p_flag =| SSWAP;
1908: rip->p_stat = SRUN;
1909: } else {
……
1919: }
呵呵,这几个函数都是我们的老朋友,只是当初都跳过了SWAP相关的部分。现在,
是了解他们的时候了。总得来说,设置SSWAP标志发生在当前进程的coremap资源得
不到满足的情况下:
(1) 对expand来说,malloc更大的coremap失败;
(2) 对xalloc来说,要“连接”的text segment的活动计数为0(总计数不为0),即该
text segment已经被swap out。于是,当前进程无法执行,则将当前进程换出;
(3) 对newproc来说,无法为新进程申请足够的core空间,于是需要将生成的新进程换出。
不但如此,这三个函数都在xswap之前调用了savu(u.u_ssav)——这使得swtch()函数的第2242行
“ aretu(u.u_ssav)”造成的影响是,当swtch()返回时,将直接返回到这三个函数的调用者。
下面,我将以expand的情况为例,来讲解其中的原因。
首先,还记得前面在讲解expand时提到的那个小技巧末?即使用xswap的第三个参数(old size),
在换出时,只换出有效的内容。如果您足够细心,您还会注意到,expand()函数换出内存image时,
在swap空间中申请的是newsize的大小。这样做的结果是,当调度进程将该进程换入时,会申请
newsize大小的core空间,即实现了expand函数的调用初衷。
接下来,我们看一下expand函数正常返回时(即core空间足够时)的逻辑路径,相比较core空间
不足的情况,多了一些代码,如下所示:
2283: if(a2 == NULL) {
… //core空间不足时
2289: }
//core空间足够时
2290: p->p_addr = a2; // 重新设置u地址
2291: for(i=0; i<n; i++) //拷贝原有u地址空间的内容
2292: copyseg(a1+i, a2++);
2293: mfree(coremap, n, a1); //释放原有u空间
2294: retu(p->p_addr); //将KISA6指向新u空间
2295: sureg(); //设置user寄存器
在expand()申请core空间失败的情况下,这些代码所做的事情会被其他的程序完成:
(1) swap out时,会free掉原有u空间;
(2) swap in时,调度进程会重设KISA6和user寄存器;而且,调度进程会
申请newsize的core空间,并将swap空间的image拷贝到core中。
因此,进程在恢复运行时,应该直接跳到expand()函数之后运行——就好像它
刚刚成功的执行了一个expand一样。而这正是由神秘的“You are not expected
to understand this”代码所完成的。
博客地址:http://blog.csdn.net/cszhao1980
博客专栏地址:http://blog.csdn.net/column/details/lions-unix.html