(莱昂氏unix源代码分析导读-26) trace

                                                         by:cszhao1980

traceunix提供的一种是父进程可以跟踪子进程进展的手段,子进程被跟踪时,当子进程

收到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:       }

 

stop4016)函数实现比较简单,它会:

(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 ptrace4164)来完成的:

【子进程】:

进行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;

 

procxmt4204)是子进程来响应父进程要求的地方,函数主体是根据ipc.ip_req的一个大switch语句。

父进程共有81~8)种要求:

11~3为取值要求,子进程会从自己空间内取一个word,放入ipc.ip_data返回;
          对于“i”、“d”不分的PDP11-40来说,12的效果是相同的;

24~6为设置要求,子进程会将ipc.ip_addr的值设置到子进程的ipc.ip_addr中;

37为信号,即 ipc.ip_data是子进程接收的signal type

48为强制退出,子进程会调用exit退出。

 

procxmt在一开始就会将ip_req0

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~6return 0,而7return 1

再次回到stop函数——return0)的后果是,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之后,子进程的STRCSWTED标记都会被清除。而即使父进程调用了ptrace

额外的两次wait()也会清除掉子进程的标记——子进程会因此从stopreturn


博客地址:http://blog.csdn.net/cszhao1980

博客专栏地址:http://blog.csdn.net/column/details/lions-unix.html


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值