- UID
- 1029342
- 性别
- 男
|
四:信号的处理
信号处理的时机:每次从内核空间返回用户空间时,都会检查当前进程是否有末处理的信号.如果有,则对信号进行处理
信号的处理函数如下:
static void fastcall do_signal(struct pt_regs *regs)
{
siginfo_t info;
int signr;
struct k_sigaction ka;
sigset_t *oldset;
//判断是否是处于返回到用户空间的前夕.不需要处理
if (!user_mode(regs))
return;
//要从task->saved_sigmask中恢复进程信号掩码
if (test_thread_flag(TIF_RESTORE_SIGMASK))
oldset = ¤t->saved_sigmask;
else
oldset = ¤t->blocked;
//对等待信号的处理
//只有遇到用户重设信号处理函数的信号或者处理完等待信号才会返回
signr = get_signal_to_deliver(&info, &ka, regs, NULL);
if (signr > 0) {
//对用户设置了信号处理函数的信号处理
if (unlikely(current->thread.debugreg[7]))
set_debugreg(current->thread.debugreg[7], 7);
/* Whee! Actually deliver the signal. */
if (handle_signal(signr, &info, &ka, oldset, regs) == 0) {
if (test_thread_flag(TIF_RESTORE_SIGMASK))
clear_thread_flag(TIF_RESTORE_SIGMASK);
}
return;
}
//没有Catch信号的系统调用重启
/* Did we come from a system call? */
if (regs->orig_eax >= 0) {
/* Restart the system call - no handlers present */
switch (regs->eax) {
case -ERESTARTNOHAND:
case -ERESTARTSYS:
case -ERESTARTNOINTR:
regs->eax = regs->orig_eax;
regs->eip -= 2;
break;
//如果是返回-ERESTART_RESTARTBLOCK ,返回用户空间后重新发起
//系统调用.系统调用号为__NR_restart_syscall
//一般用在与timer有关的系统调用中
case -ERESTART_RESTARTBLOCK:
regs->eax = __NR_restart_syscall;
regs->eip -= 2;
break;
}
}
/* if there's no signal to deliver, we just put the saved sigmask
* back */
if (test_thread_flag(TIF_RESTORE_SIGMASK)) {
//清除TIF_RESTORE_SIGMASK 并恢复信号掩码
clear_thread_flag(TIF_RESTORE_SIGMASK);
sigprocmask(SIG_SETMASK, ¤t->saved_sigmask, NULL);
}
}
正好我们在上节发送信号中所论述的一样,信号可能会引起系统调用中断.这里必须要采取必要的措施来使系统调用重启.
关于返回值与重启还是忽略如下表如示(摘自<< understanding the linux kernel >>):
Signal Action EINTR ERESTARTSYS ERESTARTNOHAND ERESTART_RESTARTBLOCK[url=mkMSITStore:E:%5CDocument%5Clinux%E5%86%85%E6%A0%B8%E8%B5%84%E6%96%99%5Cunderstanding%20the%20linux%20kernel%5COReilly.Understanding.the.Linux.Kernel.3rd.Edition.Nov.2005.HAPPY.NEW.YEAR.chm::/0596005652/understandlk-CHP-11-SECT-3.html#TFN2#TFN2]a[/url] ERESTARTNOINTR Default Terminate Reexecute Reexecute Reexecute Ignore Terminate Reexecute Reexecute Reexecute Catch Terminate Depends Terminate Reexecute
有必要关注一下上面的系统调用重启过程:
Regs参数表示用户空的硬件环境.regs->eax是表示返回用户空间后的eax寄存器的值.regs->eip是返回用户空间后执行的指针地址. regs->orig_eax是表示系统调用时eax的值,里面存放着系统调用号.请参阅本站的有关中断初始化的文档.
Regs->eip -= 2 ,为什么eip要减2呢?因为发现系统调用是int 0x80 指令.中断后,eip会指向int 80后面的一条指令.这样,如果要重新执新int 0x80.那就必须要把eip返回两条指令.
转入get_signal_to_deliver():
int get_signal_to_deliver(siginfo_t *info, struct k_sigaction *return_ka,
struct pt_regs *regs, void *cookie)
{
sigset_t *mask = ¤t->blocked;
int signr = 0;
//选择编译函数
try_to_freeze();
relock:
spin_lock_irq(¤t->sighand->siglock);
for (;;) {
struct k_sigaction *ka;
if (unlikely(current->signal->group_stop_count > 0) &&
handle_group_stop())
goto relock;
//从等待队列中取信号
signr = dequeue_signal(current, mask, info);
//信号为空,退出
if (!signr)
break; /* will return 0 */
//当前进程正在被跟踪
if ((current->ptrace & PT_PTRACED) && signr != SIGKILL) {
ptrace_signal_deliver(regs, cookie);
/* Let the debugger run. */
ptrace_stop(signr, signr, info);
/* We're back. Did the debugger cancel the sig? */
signr = current->exit_code;
if (signr == 0)
continue;
current->exit_code = 0;
/* Update the siginfo structure if the signal has
changed. If the debugger wanted something
specific in the siginfo structure then it should
have updated *info via PTRACE_SETSIGINFO. */
if (signr != info->si_signo) {
info->si_signo = signr;
info->si_errno = 0;
info->si_code = SI_USER;
info->si_pid = task_pid_vnr(current->parent);
info->si_uid = current->parent->uid;
}
/* If the (new) signal is now blocked, requeue it. */
if (sigismember(¤t->blocked, signr)) {
specific_send_sig_info(signr, info, current);
continue;
}
}
ka = ¤t->sighand->action[signr-1];
//信号被忽略,不做任何处理
if (ka->sa.sa_handler == SIG_IGN) /* Do nothing. */
continue;
//如果不为默认操作.也就是说用户已经重置了该信号的处理
//这样情况下会调用break退出循环
if (ka->sa.sa_handler != SIG_DFL) {
/* Run the handler. */
*return_ka = *ka;
//如果定义了SA_ONESHOT 标志,指明信号处理完之后,恢复信号的默认处理
if (ka->sa.sa_flags & SA_ONESHOT)
ka->sa.sa_handler = SIG_DFL;
break; /* will return non-zero "signr" value */
}
/*
* Now we are doing the default action for this signal.
*/
//如果是内核所忽略的信号,不做任何处理
//这里注意了.Child信号的默认处理是忽略.这就是形成僵尸进程
//的主要原因
if (sig_kernel_ignore(signr)) /* Default is nothing. */
continue;
/*
* Global init gets no signals it doesn't want.
*/
//判断是否是INIT 进程
if (is_global_init(current))
continue;
//引起进程挂起的信号
if (sig_kernel_stop(signr)) {
//SIGSTOP的处理与其它会引起停止的信号有点不同
//SIGSTOP总是停止进程,而其它信号只会停止不在孤儿进程组
//中的进程
if (signr != SIGSTOP) {
spin_unlock_irq(¤t->sighand->siglock);
/* signals can be posted during this window */
if (is_current_pgrp_orphaned())
goto relock;
spin_lock_irq(¤t->sighand->siglock);
}
//停止进程
if (likely(do_signal_stop(signr))) {
/* It released the siglock. */
goto relock;
}
/*
* We didn't actually stop, due to a race
* with SIGCONT or something like that.
*/
continue;
} |
|