当前位置: 首页 >> 程序设计 >> Linux信号功能的实现
 

Linux信号功能的实现

作者:      来源:zz     发表时间:2007-05-02     浏览次数:      字号:    


函数调用流程图:
kill_proc_info        kill_pg_info
     |                      |
     V                      V
kill_pid_info         __kill_pg_info
     |                      |
     |                      V
     |                __kill_pgrp_info
     |                      |
     +----------------------+
                |
                V
        group_send_sig_info
                |
                V
        __group_send_sig_info
                |
                V
            send_signal

/* kernel/signal.c */
static int send_signal(int sig, struct siginfo *info, struct task_struct *t,
   struct sigpending *signals)
{
 struct sigqueue * q = NULL;
 int ret = 0;
 /*
  * fast-pathed signals for kernel-internal things like SIGSTOP
  * or SIGKILL.
  */
// 强制发送信号(SIGKILL或SIGSTOP), 直接发送
 if (info == SEND_SIG_FORCED)
  goto out_set;
 /* Real-time signals must be queued if sent by sigqueue, or
    some other real-time mechanism.  It is implementation
    defined whether kill() does so.  We attempt to do so, on
    the principle of least surprise, but since kill is not
    allowed to fail with EAGAIN when low on memory we just
    make sure at least one signal gets delivered and don't
    pass on the info struct.  */
// 分配信号队列结构, 从sigqueue_cachep中分配(这个一个kmem_cache)
 q = __sigqueue_alloc(t, GFP_ATOMIC, (sig < SIGRTMIN &&
          (is_si_special(info) ||
           info->si_code >= 0)));
 if (q) {
// 分配成功
// 将该信号队列挂接到待发送信号链表末尾, 注意add_list是添加到链表头的
  list_add_tail(&q->list, &signals->list);
  switch ((unsigned long) info) {
// 先看是否是几个特殊的info值然后填写相应的信号信息结构参数
  case (unsigned long) SEND_SIG_NOINFO:
// 信号值
   q->info.si_signo = sig;
   q->info.si_errno = 0;
   q->info.si_code = SI_USER;
   q->info.si_pid = current->pid;
   q->info.si_uid = current->uid;
   break;
  case (unsigned long) SEND_SIG_PRIV:
// 信号值
   q->info.si_signo = sig;
   q->info.si_errno = 0;
   q->info.si_code = SI_KERNEL;
   q->info.si_pid = 0;
   q->info.si_uid = 0;
   break;
  default:
// 如是普通情况,复制信号信息
   copy_siginfo(&q->info, info);
   break;
  }
 } else if (!is_si_special(info)) {
// 队列满了,普通用户的信号就被忽略,返回错误
  if (sig >= SIGRTMIN && info->si_code != SI_USER)
  /*
   * Queue overflow, abort.  We may abort if the signal was rt
   * and sent by user using something other than kill().
   */
   return -EAGAIN;
 }
out_set:
// 设置信号集中相应的信号标志
 sigaddset(&signals->signal, sig);
 return ret;
}

/* include/asm-i386/signal.h */
#define sigaddset(set,sig)                 \
 (__builtin_constant_p(sig) ?       \
 __const_sigaddset((set),(sig)) :   \
 __gen_sigaddset((set),(sig)))
static __inline__ void __gen_sigaddset(sigset_t *set, int _sig)
{
 __asm__("btsl %1,%0" : "+m"(*set) : "Ir"(_sig - 1) : "cc");
}
static __inline__ void __const_sigaddset(sigset_t *set, int _sig)
{
 unsigned long sig = _sig - 1;
 set->sig[sig / _NSIG_BPW] |= 1 << (sig % _NSIG_BPW);
}

5. 处理信号

/* arch/i386/kernel/signal.c */
/*
 * notification of userspace execution resumption
 * - triggered by the TIF_WORK_MASK flags
 */
__attribute__((regparm(3)))
void do_notify_resume(struct pt_regs *regs, void *_unused,
        __u32 thread_info_flags)
{
 /* Pending single-step? */
 if (thread_info_flags & _TIF_SINGLESTEP) {
  regs->eflags |= TF_MASK;
  clear_thread_flag(TIF_SINGLESTEP);
 }
 /* deal with pending signal delivery */
// 派发待发送的信号
 if (thread_info_flags & (_TIF_SIGPENDING | _TIF_RESTORE_SIGMASK))
  do_signal(regs);
 
 clear_thread_flag(TIF_IRET);
}

/*
 * 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.
 */
static void fastcall do_signal(struct pt_regs *regs)
{
 siginfo_t info;
 int signr;
 struct k_sigaction ka;
 sigset_t *oldset;
 /*
  * 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.  vm86 regs switched out by assembly code
   * before reaching here, so testing against kernel
   * CS suffices.
  */
// 信号处理函数是在用户空间
 if (!user_mode(regs))
  return;
// 原先的信息集指针
 if (test_thread_flag(TIF_RESTORE_SIGMASK))
  oldset = &current->saved_sigmask;
 else
  oldset = &current->blocked;
// 找要发送的信号
 signr = get_signal_to_deliver(&info, &ka, regs, NULL);
 if (signr > 0) {
// 找到一个信号
  /* Reenable any watchpoints before delivering the
   * signal to user space. The processor register will
   * have been cleared if the watchpoint triggered
   * inside the kernel.
   */
  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) {
   /* a signal was successfully delivered; the saved
    * sigmask will have been stored in the signal frame,
    * and will be restored by sigreturn, so we can simply
    * clear the TIF_RESTORE_SIGMASK flag */
   if (test_thread_flag(TIF_RESTORE_SIGMASK))
    clear_thread_flag(TIF_RESTORE_SIGMASK);
  }
  return;
 }
// 无信号的情况
 /* 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;
  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)) {
  clear_thread_flag(TIF_RESTORE_SIGMASK);
  sigprocmask(SIG_SETMASK, &current->saved_sigmask, NULL);
 }
}

/*
 * OK, we're invoking a handler
 */ 
static int
handle_signal(unsigned long sig, siginfo_t *info, struct k_sigaction *ka,
       sigset_t *oldset, struct pt_regs * regs)
{
 int ret;
 /* Are we from a system call? */
 if (regs->orig_eax >= 0) {
// 判断当前是否在系统调用中
  /* If so, check system call restarting.. */
  switch (regs->eax) {
          case -ERESTART_RESTARTBLOCK:
   case -ERESTARTNOHAND:
    regs->eax = -EINTR;
    break;
   case -ERESTARTSYS:
    if (!(ka->sa.sa_flags & SA_RESTART)) {
     regs->eax = -EINTR;
     break;
    }
   /* fallthrough */
   case -ERESTARTNOINTR:
    regs->eax = regs->orig_eax;
    regs->eip -= 2;
  }
 }
 /*
  * If TF is set due to a debugger (PT_DTRACE), clear the TF flag so
  * that register information in the sigcontext is correct.
  */
 if (unlikely(regs->eflags & TF_MASK)
     && likely(current->ptrace & PT_DTRACE)) {
  current->ptrace &= ~PT_DTRACE;
  regs->eflags &= ~TF_MASK;
 }
 /* Set up the stack frame */
// 建立进入信号处理函数前堆栈帧
 if (ka->sa.sa_flags & SA_SIGINFO)
  ret = setup_rt_frame(sig, ka, info, oldset, regs);
 else
  ret = setup_frame(sig, ka, oldset, regs);
 if (ret == 0) {
  spin_lock_irq(&current->sighand->siglock);
  sigorsets(&current->blocked,&current->blocked,&ka->sa.sa_mask);
  if (!(ka->sa.sa_flags & SA_NODEFER))
   sigaddset(&current->blocked,sig);
  recalc_sigpending();
  spin_unlock_irq(&current->sighand->siglock);
 }
 return ret;
}
 
/* kernel/signal.c */
// 找要发送的信号
int get_signal_to_deliver(siginfo_t *info, struct k_sigaction *return_ka,
     struct pt_regs *regs, void *cookie)
{
 sigset_t *mask = &current->blocked;
 int signr = 0;
 try_to_freeze();
relock:
 spin_lock_irq(&current->sighand->siglock);
 for (;;) {
  struct k_sigaction *ka;
// unlikely的处理就是大部分情况下可忽略
  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 = current->parent->pid;
    info->si_uid = current->parent->uid;
   }
   /* If the (new) signal is now blocked, requeue it.  */
   if (sigismember(&current->blocked, signr)) {
    specific_send_sig_info(signr, info, current);
    continue;
   }
  }
// 获取当前进程的信号处理函数
  ka = &current->sighand->action[signr-1];
  if (ka->sa.sa_handler == SIG_IGN) /* Do nothing.  */
// 该信号是要忽略的,重新找下一信号
   continue;
  if (ka->sa.sa_handler != SIG_DFL) {
// 定义了具体的函数处理函数, 复制要返回信号处理结构
   /* Run the handler.  */
   *return_ka = *ka;
   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.
   */
// 否则执行系统缺省的信号处理
  if (sig_kernel_ignore(signr)) /* Default is nothing. */
   continue;
  /* Init gets no signals it doesn't want.  */
  if (current == child_reaper)
   continue;
  if (sig_kernel_stop(signr)) {
   /*
    * The default action is to stop all threads in
    * the thread group.  The job control signals
    * do nothing in an orphaned pgrp, but SIGSTOP
    * always works.  Note that siglock needs to be
    * dropped during the call to is_orphaned_pgrp()
    * because of lock ordering with tasklist_lock.
    * This allows an intervening SIGCONT to be posted.
    * We need to check for that and bail out if necessary.
    */
   if (signr != SIGSTOP) {
    spin_unlock_irq(&current->sighand->siglock);
    /* signals can be posted during this window */
    if (is_orphaned_pgrp(process_group(current)))
     goto relock;
    spin_lock_irq(&current->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;
  }
  spin_unlock_irq(&current->sighand->siglock);
  /*
   * Anything else is fatal, maybe with a core dump.
   */
  current->flags |= PF_SIGNALED;
  if (sig_kernel_coredump(signr)) {
   /*
    * If it was able to dump core, this kills all
    * other threads in the group and synchronizes with
    * their demise.  If we lost the race with another
    * thread getting here, it set group_exit_code
    * first and our do_group_exit call below will use
    * that value and ignore the one we pass it.
    */
   do_coredump((long)signr, signr, regs);
  }
  /*
   * Death signals, no core dump.
   */
  do_group_exit(signr);
  /* NOTREACHED */
 }
 spin_unlock_irq(&current->sighand->siglock);
 return signr;
}

6. 结论

Linux下的信号实现基本符合了APUE中对信号处理的描述, SIGKILL/SIGSTOP不可忽略, 发送给某进程
的信号会排队, 信号可能会丢失等等.

[1] [2]

责任编辑 webmaster

 
发表评论  打印本文  推荐本文  加入收藏  返回顶部  关闭窗口
 
 
 
 
评论更多>>
 
 
 
发表
 
姓名: QQ:
性别: MSN:
E-mail: 主页:
评分: 1 2 3 4 5
评论内容:
验证码:
  
  • 请遵守《互联网电子公告服务管理规定》及中华人民共和国其他各项有关法律法规。
  • 严禁发表危害国家安全、损害国家利益、破坏民族团结、破坏国家宗教政策、破坏社会稳定、侮辱、诽谤、教唆、淫秽等内容的评论 。
  • 用户需对自己在使用本站服务过程中的行为承担法律责任(直接或间接导致的)。
  • 本站管理员有权保留或删除评论内容。
  • 评论内容只代表网友个人观点,与本网站立场无关。
  •