by:cszhao1980
trace是unix提供的一种是父进程可以跟踪子进程进展的手段,子进程被跟踪时,当子进程
收到signal后,会进入“暂停(SSTOP)”状态,使父进程有机会进行干预。子进程的暂停
是在issig()函数中实现的:
3997: if(n = p->p_sig) {
3998: if (p->p_flag&STRC) { /进程处于trace模式,以后再详细解释
3999: stop();
4000: if ((n = p->p_sig) == 0)
4001: return(0);
4002: }
stop(4016)函数实现比较简单,它会:
(1) Loop 进程表,找到其父进程,然后调用wakeup(父进程进程表项地址)将其唤醒:
(2) 将本进程的status设置为SSTOP;
(3) 调用swtch()切换进程;
(4) 从swtch返回(进程被唤醒)后动作有些让人不解,我们稍后再说。
trace的实现需要父进程和子进程相互配合:
(1) 首先,子进程要允许自己被trace,而后就像刚才所说的那样,在收到signal后会“暂停”;
(2) 而父进程也需要主动去查看子进程的状态,一旦其处于暂停状态就通过ipc结构传递信
息,并以此指挥子进程的行动。
3933: struct
3934: {
3935: int ip_lock; /锁定标记
3936: int ip_req; /父进程对子进程的“要求”,子进程据此“展开行动”
3937: int ip_addr;
3938: int ip_data;
3939: } ipc;
子进程和父进程的动作都是通过sys call ptrace(4164)来完成的:
【子进程】:
进行sys call时,参数u.arg(2)<=0,表示是子进程要求自己被trace,在进程flag上添加STRC标记,然后return;
【父进程】:
进行sys call时,参数u.arg(2) >0;参数u.arg(0)为指定的子进程的id;
(1) Loop进程表,找到指定进程(子进程,id==u.arg(0),状态为SSTOP);
(2) 接下来就是在ipc结构里传入各种参数;
4183: ipc.ip_lock = p->p_pid; /锁是子进程id
4184: ipc.ip_data = u.u_ar0[R0];
4185: ipc.ip_addr = u.u_arg[1] & ~01;
4186: ipc.ip_req = u.u_arg[2];
(3) clear子进程的SWTED标记;
4187: p->p_flag =& ~SWTED;
(4) 激活子进程
4188: setrun(p);
(5) 父进程睡眠,直到ipc.ipreq<=0
4189: while (ipc.ip_req > 0)
4190: sleep(&ipc, IPCPRI);
到现在为止,相信大家对父进程的处理感到很迷惑,没关系,让我们回头看看子进程。子进程醒来后,
会返回到stop函数的swtch语句之后,如下所示:
4016: stop()
4020:loop:
4021: cp = u.u_procp;
4022: if(cp->p_ppid != 1)
4023: for (pp = &proc[0]; pp < &proc[NPROC]; pp++)
4024: if (pp->p_pid == cp->p_ppid) {
4025: wakeup(pp);
4026: cp->p_stat = SSTOP;
4027: swtch();
4028: if ((cp->p_flag&STRC)==0 || procxmt())
4029: return;
4030: goto loop;
procxmt(4204)是子进程来响应父进程要求的地方,函数主体是根据ipc.ip_req的一个大switch语句。
父进程共有8(1~8)种要求:
(1)1~3为取值要求,子进程会从自己空间内取一个word,放入ipc.ip_data返回;
对于“i”、“d”不分的PDP11-40来说,1和2的效果是相同的;
(2)4~6为设置要求,子进程会将ipc.ip_addr的值设置到子进程的ipc.ip_addr中;
(3)7为信号,即 ipc.ip_data是子进程接收的signal type;
(4)8为强制退出,子进程会调用exit退出。
procxmt在一开始就会将ip_req清0:
4212: ipc.ip_req = 0;
4213: wakeup(&ipc); /激活了睡眠的父进程
而父进程醒后,处在ptrace函数中:
4189: while (ipc.ip_req > 0)
4190: sleep(&ipc, IPCPRI);
4191: u.u_ar0[R0] = ipc.ip_data; /返回值
4192: if (ipc.ip_req < 0)
4193: u.u_error = EIO;
4194: ipc.ip_lock = 0; /清除锁
4195: wakeup(&ipc); /激活因此睡眠的进程
根据父进程要求的不同,procxmt的返回值也不同:1~6会return 0,而7会return 1。
再次回到stop函数——return(0)的后果是,stop不会return,而是再次使进程睡眠。
为什么要返回0,致使子进程在stop中再次进入睡眠?
我不理解。
另一个处理trace的地方是wait()函数:
3301: if(p->p_stat == SSTOP) {
3302: if((p->p_flag&SWTED) == 0) {
3303: p->p_flag =| SWTED;
3304: u.u_ar0[R0] = p->p_pid;
3305: u.u_ar0[R1] = (p->p_sig<<8) | 0177;
3306: return;
3307: }
3308: p->p_flag =& ~(STRC|SWTED);
3309: setrun(p);
3310: }
这个奇怪的检查也很难理解,按照莱昂的解释,SWTED的设置是为了处理父进程不调用ptrace的情况,
此种情况下,两次wait之后,子进程的STRC和SWTED标记都会被清除。而即使父进程调用了ptrace,
额外的两次wait()也会清除掉子进程的标记——子进程会因此从stop中return。
博客地址:http://blog.csdn.net/cszhao1980
博客专栏地址:http://blog.csdn.net/column/details/lions-unix.html