1)查找指定信号由do_signal()->get_signal_to_deliver()完成
int get_signal_to_deliver(siginfo_t *info,struct k_sigaction *return_ka,
struct pt_regs *regs, void *cookie)
{
struct sighand_struct *sighand = current->sighand;
struct signal_struct *signal = current->signal;
int signr;
relock:
……
for (;;) {
struct k_sigaction *ka;
……
signr = tracehook_get_signal(current, regs, info, return_ka);
if (unlikely(signr < 0))
goto relock;
if (unlikely(signr != 0))
ka = return_ka;
else {
/*从挂起队列中获取信号*/
signr = dequeue_signal(current, ¤t->blocked,
info);
……
ka = &sighand->action[signr-1];/*找到对应的action*/
}
if (ka->sa.sa_handler == SIG_IGN) /* Do nothing. */
continue;
if (ka->sa.sa_handler != SIG_DFL) {
/*将找到的action设置为参数返回*/
*return_ka = *ka;
if (ka->sa.sa_flags & SA_ONESHOT)
ka->sa.sa_handler = SIG_DFL;
break; /* will return non-zero "signr" value */
}
……
}//end for
spin_unlock_irq(&sighand->siglock);
return signr;
}
int dequeue_signal(struct task_struct *tsk, sigset_t *mask, siginfo_t *info)
{
int signr;
/*先从私有挂起队列中寻找*/
signr = __dequeue_signal(&tsk->pending, mask, info);
if (!signr) {
/*当私有挂起队列中找不到时,从共享队列中找*/
signr = __dequeue_signal(&tsk->signal->shared_pending,
mask, info);
……
}
……
return signr;
}
内核先从自己私有的挂起信号队列中查找,当找不到时,再从共享信号队列中查找,以参数的形式返回找到的action。
2)信号处理do_signal()->handle_signal()
staticint
handle_signal(unsignedlong sig, siginfo_t *info, struct k_sigaction *ka,
sigset_t *oldset, struct pt_regs *regs)
{
int ret;
/* Are we from a system call? */
if (syscall_get_nr(current, regs) >= 0) {
/* If so, check system call restarting.. */
switch (syscall_get_error(current, regs)) {
case -ERESTART_RESTARTBLOCK:
case -ERESTARTNOHAND:
regs->ax = -EINTR;
break;
case -ERESTARTSYS:
if (!(ka->sa.sa_flags & SA_RESTART)) {
regs->ax = -EINTR;
break;
}
/* fallthrough */
case -ERESTARTNOINTR:
regs->ax = regs->orig_ax;
regs->ip -= 2;
break;
}
}
……
/*实际处理*/
ret = setup_rt_frame(sig, ka, info, oldset, regs);
if (ret)
return ret;
……
return 0;
}
上面的setup_rt_frame()在Intel32体系下会最终调用下面函数完成。
int ia32_setup_frame(int sig,struct k_sigaction *ka,
compat_sigset_t *set,struct pt_regs *regs)
{
struct sigframe_ia32 __user *frame;
void __user *restorer;
int err = 0;
void __user *fpstate = NULL;
/* copy_to_user optimizes that into a single 8 byte store */
/*设置ia32_sigreturn系统调用,用于返回*/
staticconst struct {
u16 poplmovl;
u32 val;
u16 int80;
} __attribute__((packed)) code = {
0xb858, /* popl %eax ; movl $...,%eax */
__NR_ia32_sigreturn,
0x80cd, /* int $0x80 */
};
/*返回为栈中的一部分数据*/
frame = get_sigframe(ka, regs,sizeof(*frame), &fpstate);
……
/*设置为用户空间的各种寄存器,完成设置后,由于ip设置为handler处,所以调到该处执行*/
/* Set up registers for signal handler */
regs->sp = (unsignedlong) frame;
regs->ip = (unsignedlong) ka->sa.sa_handler;
/* Make -mregparm=3 work */
regs->ax = sig;
regs->dx = 0;
regs->cx = 0;
loadsegment(ds, __USER32_DS);
loadsegment(es, __USER32_DS);
regs->cs = __USER32_CS;
regs->ss = __USER32_DS;
return 0;
}
终于看到内核怎么处理了吧,由于信号的handler为用户空间程序,系统需要返回到用户空间执行,所以我们看到,内核直接设置ip为handler,代码段cs为用户空间代码段,数据段ds、es以及栈ss等都设置为用户空间的,该函数完成后,程序将冲用户空间的handler处开始执行。上面程序设置ia32_sigreturn系统调用作为程序返回的调用。整体的运行原理图如下(该图参考《深入理解Linux内核框架》 ):
Linux内核信号机制用到很多地方,最常见的是进程间通信,原理较简单,并且异步调用。