内核信号实现

信号的概念大家不陌生。经常使用的kill命令,可以先运行的进程发送信号,运行进程在收到信号后,做出相应的处理。在用户态程序中关于信号的编程,主要有如下的特点

1、程序可以给不同的信号安装特定的handler程序,这个handler有一定的格式,由用户程序提供

2、应用程序可以配置,阻塞一些信号,使得程序可以不响应这些信号

3、信号一般有默认的handler处理流程,应用程序如果不主动设置handler,则使用系统默认的handler

4、有一些信号,用户不能设置handler也不能阻塞

 

一般的用户态信号变成模式如下

 

我们不去分析用户态如何进行信号的编程,和信号相关的一些应用。而主要分析,信号在内核中的实现。主要有三个方面的内容。

1、信号相关的内核数据结构

2、发送信号到进程的过程

3、信号处理过程

 

信号相关的内核数据结构

 

进程的数据结构task_struct中保持了信号相关的数据结构,一个是pending,这个结构上保存了进程上尚未处理的信号。一个是sighand,这个结构上保存了信号对应的action。

struct sigpending      struct list_head list;      sigset_t signal;  }; 

 

struct sigqueue      struct list_head list;      int flags;      siginfo_t info;      struct user_struct *user;  }; 

 

struct sighand_struct      atomic_t        count;      struct k_sigaction    action[_NSIG];      spinlock_t        siglock;      wait_queue_head_t    signalfd_wqh;  }; 

 

发送信号到进程的过程

 

上面函数调用过程是发送信号到进程的过程。首先find_task_by_vpid通过pid找打对于的进程task_struct数据结构。然后调用send_signal函数进行信号发送,具体过程见下面的分析。

static int send_signal(int sig, struct siginfo *info, struct task_struct *t,                         int group)      struct sigpending *pending;      struct sigqueue *q;        trace_sched_signal_send(sig, t);        assert_spin_locked(&t->sighand->siglock);      /*prepare_signal函数会调用sig_ignored检查信号是否被标记会堵塞        如果被标记为堵塞,就不处理了*/      if (!prepare_signal(sig, t))          return 0       pending = group ? &t->signal->shared_pending : &t->pending;      /*       * Short-circuit ignored signals and support queuing       * exactly one non-rt signal, so that we can get more       * detailed information about the cause of the signal.       */      if (legacy_queue(pending, sig))          return 0     /*       * fast-pathed signals for kernel-internal things like SIGSTOP       * or SIGKILL.       */      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 结构图*/      q = __sigqueue_alloc(t, GFP_ATOMIC, (sig < SIGRTMIN &&                                           (is_si_special(info) ||                                            info->si_code >= 0)));      if (q)      {          /*将sigqueue结构体挂接到task_struct pending队列上面*/          list_add_tail(&q->list, &pending->list);          switch ((unsigned long) 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 = task_tgid_nr_ns(current,                                               task_active_pid_ns(t));              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:      signalfd_notify(t, sig);      sigaddset(&pending->signal, sig);      /*complete_signal函数唤醒休眠的进程*/      complete_signal(sig, t, group);      return 0

 

下面是唤醒休眠进程进行信号处理的过程,这里要主要,进程如果处于TASK_UNINTERRUPTIBLE状态,则进程不能被唤醒。这也是有时候用kill命令无法杀掉一些休眠的进程的原因。

void signal_wake_up(struct task_struct *t, int resume)      unsigned int mask;        /*设置进程为 sigpending状态*/      set_tsk_thread_flag(t, TIF_SIGPENDING);        /*       * For SIGKILL, we want to wake it up in the stopped/traced/killable       * case. We don't check t->state here because there is a race with it       * executing another processor and just now entering stopped state.       * By using wake_up_state, we ensure the process will wake up and       * handle its death signal.       */      mask = TASK_INTERRUPTIBLE;      if (resume)          mask |= TASK_WAKEKILL;      /*唤醒状态为 TASK_INTERRUPTIBLE的进程,这里特别要主要的是        TASK_UNINTERRUPTIBLE的进程不会被唤醒*/      if (!wake_up_state(t, mask))          kick_process(t); 

 

信号处理过程

 

进程处理信号的函数是do_signal,调用这个函数的位置是,进程从内核态进入到用户态时会调用这个函数。调用信号的handler在用户态执行,执行完之后通过胶粘代码再次回到内核态来。
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值