首页 | 新闻 | 新品 | 文库 | 方案 | 视频 | 下载 | 商城 | 开发板 | 数据中心 | 座谈新版 | 培训 | 工具 | 博客 | 论坛 | 百科 | GEC | 活动 | 主题月 | 电子展
返回列表 回复 发帖

linux进程管理之信号处理(转)(7)

linux进程管理之信号处理(转)(7)

setup_sigcontext()代码如下:
static int
setup_sigcontext(struct sigcontext __user *sc, struct _fpstate __user *fpstate,
          struct pt_regs *regs, unsigned long mask)
{
     int tmp, err = 0;


     //保存regs
     err |= __put_user(regs->xfs, (unsigned int __user *)&sc->fs);
     savesegment(gs, tmp);
     err |= __put_user(tmp, (unsigned int __user *)&sc->gs);

     err |= __put_user(regs->xes, (unsigned int __user *)&sc->es);
     err |= __put_user(regs->xds, (unsigned int __user *)&sc->ds);
     err |= __put_user(regs->edi, &sc->edi);
     err |= __put_user(regs->esi, &sc->esi);
     err |= __put_user(regs->ebp, &sc->ebp);
     err |= __put_user(regs->esp, &sc->esp);
     err |= __put_user(regs->ebx, &sc->ebx);
     err |= __put_user(regs->edx, &sc->edx);
     err |= __put_user(regs->ecx, &sc->ecx);
     err |= __put_user(regs->eax, &sc->eax);
     err |= __put_user(current->thread.trap_no, &sc->trapno);
     err |= __put_user(current->thread.error_code, &sc->err);
     err |= __put_user(regs->eip, &sc->eip);
     err |= __put_user(regs->xcs, (unsigned int __user *)&sc->cs);
     err |= __put_user(regs->eflags, &sc->eflags);
     err |= __put_user(regs->esp, &sc->esp_at_signal);
     err |= __put_user(regs->xss, (unsigned int __user *)&sc->ss);

     //保存FPU,XMM.MXX等信息
     tmp = save_i387(fpstate);
     if (tmp < 0)
       err = 1;
     else
       err |= __put_user(tmp ? fpstate : NULL, &sc->fpstate);

     /* non-iBCS2 extensions.. */

     //mask:即为set->sig 的低32位
     err |= __put_user(mask, &sc->oldmask);
     err |= __put_user(current->thread.cr2, &sc->cr2);

     return err;
}
用下图表示上述的操作:



注意到代码中有以下两条指令:
regs->esp = (unsigned long) frame;
regs->eip = (unsigned long) ka->sa.sa_handler;
第一条把用户的栈指令指向了frame


第二条把返回用户空间的eip设为了信号的处理函数.
这样返回到用户空间后就会执行ka->sa.sa_handler这个函数.注意到上面的堆栈结构,其实它模拟了一次函数调用.函数调用时,先把参数压栈,再把返回地址压栈.在上面的栈中,函数的参数为sig.返回地址为pretcode.这样,在信号处理函数返回之后.就会把pretcode装入eip.而pretcode又是指向retcode.也就是说函数返回之后,会运行retcode对应的指令.
Retcode在上面的代码中是这样被设置的:
     err |= __put_user(0xb858, (short __user *)(frame->retcode+0));
     err |= __put_user(__NR_sigreturn, (int __user *)(frame->retcode+2));
     err |= __put_user(0x80cd, (short __user *)(frame->retcode+6));
代码中的0xb858 0x80cd可能对应的就是指令的机器码.它相应于如下指令:
popl %eax ;
movl $,%eax ;
int $0x80
即会产生一个系统调用号为__NR_sigreturn的系统调用.它对应的入口是:
asmlinkage int sys_sigreturn(unsigned long __unused)
{
     //第一个参数地址就是栈指针位置
     struct pt_regs *regs = (struct pt_regs *) &__unused;
     //esp-8是因为在用户空间运行的时候,栈出了两个单元
     //即上图中的pretcode出栈.sig出栈
     struct sigframe __user *frame = (struct sigframe __user *)(regs->esp - 8);
     sigset_t set;
     int eax;

     //检查对应区域是否可读
     if (!access_ok(VERIFY_READ, frame, sizeof(*frame)))
         goto badframe;

     //从frame->sc.oldmask 恢复set.sig的低32 位
     if (__get_user(set.sig[0], &frame->sc.oldmask)
         || (_NSIG_WORDS > 1
         //从frame->extramask 中恢复set.sig的高32位
         && __copy_from_user(&set.sig[1], &frame->extramask,
                       sizeof(frame->extramask))))
         goto badframe;

     sigdelsetmask(&set, ~_BLOCKABLE);
     spin_lock_irq(¤t->sighand->siglock);
     current->blocked = set;

     //重新判断是否还有末处理的信号
     recalc_sigpending();
     spin_unlock_irq(¤t->sighand->siglock);


     //从frame->sc中恢复系统调用前的硬件环境
     if (restore_sigcontext(regs, &frame->sc, &eax))
         goto badframe;
     return eax;

badframe:
     if (show_unhandled_signals && printk_ratelimit())
         printk("%s%s[%d] bad frame in sigreturn frame:%p eip:%lx"
                " esp:%lx oeax:%lx\n",
             task_pid_nr(current) > 1 ? KERN_INFO : KERN_EMERG,
             current->comm, task_pid_nr(current), frame, regs->eip,
             regs->esp, regs->orig_eax);

     force_sig(SIGSEGV, current);
     return 0;
}
至此,内核栈又回复到以前的样子了.
五:小结
本节中,在Linux内核中跟踪了信号处理函数的设置,信号的发送.信号的处理.涉及到的代码都不是很难理解.在理解了用户自定义的信号函数的运行机制之后,我们也很容易调用用户空间的一个特定操作.另外,虽然内核涉及到的信号处理比较简单,但要在用户空间使用好信号就要看一个人的程序设计功底了.
继承事业,薪火相传
返回列表