Linux当中信号的实现机制

1220阅读 0评论2020-09-28 stolennnxb
分类:LINUX

1. 发送信号
内核当中信号相关的成员如下所示

点击(此处)折叠或打开

  1. struct task_struct{
  2. ...
  3.     /* Signal handlers: */
  4.     struct signal_struct        *signal;
  5.     struct sighand_struct __rcu        *sighand;
  6.     sigset_t            blocked;
  7.     sigset_t            real_blocked;
  8.     /* Restored if set_restore_sigmask() was used: */
  9.     sigset_t            saved_sigmask;
  10.     struct sigpending        pending;
  11.     unsigned long            sas_ss_sp;
  12.     size_t                sas_ss_size;
  13.     unsigned int            sas_ss_flags;
  14. ...
  15. };
其中pending表示挂在当前task_struct上的信号,实现时一个链表和一个位图


点击(此处)折叠或打开

  1. struct sigpending {
  2.     struct list_head list;
  3.     sigset_t signal;
  4. };
两个元素都表示外挂信号,不同点就在于排队规则的不同:

点击(此处)折叠或打开

  1. static inline bool legacy_queue(struct sigpending *signals, int sig)
  2. {
  3.     return (sig < SIGRTMIN) && sigismember(&signals->signal, sig);
  4. }

也就是说,如果发送的信号小于SIGRTMIN并且已经在外挂信号集合当中,则直接返回,不做处理;如果相反,则挂在链表当中,也就是说以SIGRTMIN为分界线,信号可以分为可丢和排队的两种。

发送完信号就会将对应的task_struct设置TIF_SIGPENDING标志,并且试图唤醒之
2. 处理信号
当一个进程接收到一个信号的时候,并不是立即处理的,而是设置其TIF_SIGPENDING,在其调用schedule的时候进行处理。处理的时候,由于是从系统调用或者中断返回,这里就需要点trick来实现,返回的时候,如果没有信号,则直接返回到进入系统到用的下一条指令即可,但是有信号,需要先执行信号处理函数,然后再返回进入到系统调用的下一条指令。linux当中的做法就是在返回之前,在用户态栈当中插入一个栈帧,栈帧的执行顺序就是保护之前上下文——>设置指令到信号处理函数——>设置处理函数执行完毕之后跳转到恢复函数——>恢复函数调用rt_sigreturn再次进入内核——>内核当中恢复上下文——>返回系统调用
上一篇:c++中的cast碎碎念
下一篇:linux用户态ipc实现机制