日期:2014-05-16  浏览次数:20774 次

linux内核中的信号机制--信号处理

linux内核中的信号机制--信号处理

Kernel version:2.6.14

CPU architecture:ARM920T

Author:ce123(http://blog.csdn.net/ce123)

当进程被调度时,会调用do_notify_resume()来处理信号队列中的信号。信号处理主要就是调用sighand_struct结构中对应的信号处理函数。do_notify_resume()(arch/arm/kernel/signal.c)函数的定义如下:

asmlinkage void
do_notify_resume(struct pt_regs *regs, unsigned int thread_flags, int syscall)
{
	if (thread_flags & _TIF_SIGPENDING)
		do_signal(&current->blocked, regs, syscall);
}
_TIF_SIGPENDING标志是在signal_wake_up()函数中设置的,检查该标志后,接下来就调用do_signal()函数,我们来看看do_signal()(arch/arm/kernel/signal.c)的具体定义:

/*
 * Note that 'init' is a special process: it doesn't get signals it doesn't
 * want to handle. Thus you cannot kill init even with a SIGKILL even by
 * mistake.
 *
 * Note that we go through the signals twice: once to check the signals that
 * the kernel can handle, and then we build all the user-level signal handling
 * stack-frames in one go after that.
 */
static int do_signal(sigset_t *oldset, struct pt_regs *regs, int syscall)
{
	struct k_sigaction ka;
	siginfo_t info;
	int signr;

	/*
	 * We want the common case to go fast, which
	 * is why we may in certain cases get here from
	 * kernel mode. Just return without doing anything
	 * if so.
	 */
	if (!user_mode(regs))//regs保存的是进入内核态之前的寄存器现场,必须为用户模式,否则直接返回
		return 0;

	if (try_to_freeze())
		goto no_signal;

	if (current->ptrace & PT_SINGLESTEP)
		ptrace_cancel_bpt(current);//和调试相关,我们在后面的文章中会具体分析

	signr = get_signal_to_deliver(&info, &ka, regs, NULL);//取出等待处理的信号
	if (signr > 0) {
		handle_signal(signr, &ka, &info, oldset, regs, syscall);//处理信号
		if (current->ptrace & PT_SINGLESTEP)
			ptrace_set_bpt(current);
		return 1;
	}

 no_signal:
	/*
	 * No signal to deliver to the process - restart the syscall.
	 */
	if (syscall) {
		if (regs->ARM_r0 == -ERESTART_RESTARTBLOCK) {
			if (thumb_mode(regs)) {
				regs->ARM_r7 = __NR_restart_syscall;
				regs->ARM_pc -= 2;
			} else {
				u32 __user *usp;

				regs->ARM_sp -= 12;
				usp = (u32 __user *)regs->ARM_sp;

				put_user(regs->ARM_pc, &usp[0]);
				/* swi __NR_restart_syscall */
				put_user(0xef000000 | __NR_restart_syscall, &usp[1]);
				/* ldr	pc, [sp], #12 */
				put_user(0xe49df00c, &usp[2]);

				flush_icache_range((unsigned long)usp,
						   (unsigned long)(usp + 3));

				regs->ARM_pc = regs->ARM_sp + 4;
			}
		}
		if (regs->ARM_r0 == -ERESTARTNOHAND ||
		    regs->ARM_r0 == -ERESTARTSYS ||
		    regs->ARM_r0 == -ERESTA