1. 发送信号
内核当中信号相关的成员如下所示
-
struct task_struct{
-
...
-
/* Signal handlers: */
-
struct signal_struct *signal;
-
struct sighand_struct __rcu *sighand;
-
sigset_t blocked;
-
sigset_t real_blocked;
-
/* Restored if set_restore_sigmask() was used: */
-
sigset_t saved_sigmask;
-
struct sigpending pending;
-
unsigned long sas_ss_sp;
-
size_t sas_ss_size;
-
unsigned int sas_ss_flags;
-
...
-
};
其中pending表示挂在当前task_struct上的信号,实现时一个链表和一个位图
-
struct sigpending {
-
struct list_head list;
-
sigset_t signal;
-
};
两个元素都表示外挂信号,不同点就在于排队规则的不同:
-
static inline bool legacy_queue(struct sigpending *signals, int sig)
-
{
-
return (sig < SIGRTMIN) && sigismember(&signals->signal, sig);
-
}
也就是说,如果发送的信号小于SIGRTMIN并且已经在外挂信号集合当中,则直接返回,不做处理;如果相反,则挂在链表当中,也就是说以SIGRTMIN为分界线,信号可以分为可丢和排队的两种。
发送完信号就会将对应的task_struct设置TIF_SIGPENDING标志,并且试图唤醒之
2. 处理信号
当一个进程接收到一个信号的时候,并不是立即处理的,而是设置其TIF_SIGPENDING,在其调用schedule的时候进行处理。处理的时候,由于是从系统调用或者中断返回,这里就需要点trick来实现,返回的时候,如果没有信号,则直接返回到进入系统到用的下一条指令即可,但是有信号,需要先执行信号处理函数,然后再返回进入到系统调用的下一条指令。linux当中的做法就是在返回之前,在用户态栈当中插入一个栈帧,栈帧的执行顺序就是保护之前上下文——>设置指令到信号处理函数——>设置处理函数执行完毕之后跳转到恢复函数——>恢复函数调用rt_sigreturn再次进入内核——>内核当中恢复上下文——>返回系统调用
阅读(1270) | 评论(0) | 转发(0) |