接前一篇文章:Linux内核进程管理子系统有什么第二十五回 —— 进程主结构详解(21)
本文内容参考:
Linux内核进程管理专题报告_linux rseq-CSDN博客
《趣谈Linux操作系统 核心原理篇:第三部分 进程管理》—— 刘超
《图解Linux内核 基于6.x》 —— 姜亚华 机械工业出版社
特此致谢!
进程管理核心结构 —— task_struct
3. 信号处理相关成员
上一回继续讲解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;
上一回沿以下路径深入跟进:
do_send_sig_info函数 --->
send_signal_locked函数 --->
__send_signal_locked函数 --->
complete_signal函数
讲到了__send_signal_locked函数的最后一步 —— complete_signal函数。
(6)调用complete_signal函数查找一个处理信号的进程(线程)
上一回解析了complete_signal函数的第1步,本回继续解析后续内容。为了便于理解和回顾,再次贴出complete_signal函数源码,在同文件(kernal/signal.c)中,如下:
static void complete_signal(int sig, struct task_struct *p, enum pid_type type)
{
struct signal_struct *signal = p->signal;
struct task_struct *t;
/*
* Now find a thread we can wake up to take the signal off the queue.
*
* If the main thread wants the signal, it gets first crack.
* Probably the least surprising to the average bear.
*/
if (wants_signal(sig, p))
t = p;
else if ((type == PIDTYPE_PID) || thread_group_empty(p))
/*
* There is just one thread and it does not need to be woken.
* It will dequeue unblocked signals before it runs again.
*/
return;
else {
/*
* Otherwise try to find a suitable thread.
*/
t = signal->curr_target;
while (!wants_signal(sig, t)) {
t = next_thread(t);
if (t == signal->curr_target)
/*
* No thread needs to be woken.
* Any eligible threads will see
* the signal in the queue soon.
*/
return;
}
signal->curr_target = t;
}
/*
* Found a killable thread. If the signal will be fatal,
* then start taking the whole group down immediately.
*/
if (sig_fatal(p, sig) &&
(signal->core_state || !(signal->flags & SIGNAL_GROUP_EXIT)) &&
!sigismember(&t->real_blocked, sig) &&
(sig == SIGKILL || !p->ptrace)) {
/*
* This signal will be fatal to the whole group.
*/
if (!sig_kernel_coredump(sig)) {
/*
* Start a group exit and wake everybody up.
* This way we don't have other threads
* running and doing things after a slower
* thread has the fatal signal pending.
*/
signal->flags = SIGNAL_GROUP_EXIT;
signal->group_exit_code = sig;
signal->group_stop_count = 0;
t = p;
do {
task_clear_jobctl_pending(t, JOBCTL_PENDING_MASK);
sigaddset(&t->pending.signal, SIGKILL);
signal_wake_up(t, 1);
} while_each_thread(p, t);
return;
}
}
/*
* The signal is already in the shared-pending queue.
* Tell the chosen thread to wake up and dequeue it.
*/
signal_wake_up(t, sig == SIGKILL);
return;
}
complete_signal函数分为以下步骤:
1)查找一个可以处理信号的进程
2)发现了一个可杀死的线程。如果信号是致命的,那么立即开始关闭整个线程组
代码片段如下:
/*
* Found a killable thread. If the signal will be fatal,
* then start taking the whole group down immediately.
*/
if (sig_fatal(p, sig) &&
(signal->core_state || !(signal->flags & SIGNAL_GROUP_EXIT)) &&
!sigismember(&t->real_blocked, sig) &&
(sig == SIGKILL || !p->ptrace)) {
/*
* This signal will be fatal to the whole group.
*/
if (!sig_kernel_coredump(sig)) {
/*
* Start a group exit and wake everybody up.
* This way we don't have other threads
* running and doing things after a slower
* thread has the fatal signal pending.
*/
signal->flags = SIGNAL_GROUP_EXIT;
signal->group_exit_code = sig;
signal->group_stop_count = 0;
t = p;
do {
task_clear_jobctl_pending(t, JOBCTL_PENDING_MASK);
sigaddset(&t->pending.signal, SIGKILL);
signal_wake_up(t, 1);
} while_each_thread(p, t);
return;
}
}
如果信号属于sig_fatal,并且满足&&后边的一系列条件,则在信号不属于sig_kernel_coredump的情况下,发送SIGKILL到线程组中的每一个线程,整个组退出。
哪些信号属于sig_fatal即致命信号呢?1)信号不属于sig_kernel_ignore;2)信号不属于sig_kernel_stop;3)信号的处理方式是SIG_DFL。也就是说,默认行为是terminate,且用户未改变处理方式的信号。
这里顺带提一下:kill不能发送信号到指定线程,但pthread_kill可以。它是通过tgkill系统调用实现的,和kill不同的是,它调用do_send_sig_info时传递的type参数为PIDTYPE_PID。
3)唤醒步骤1)中找到的可以处理信号的进程或线程
代码片段如下:
/*
* The signal is already in the shared-pending queue.
* Tell the chosen thread to wake up and dequeue it.
*/
signal_wake_up(t, sig == SIGKILL);
signal_wake_up函数在include/linux/sched/signal.h中,代码如下:
static inline void signal_wake_up(struct task_struct *t, bool fatal)
{
unsigned int state = 0;
if (fatal && !(t->jobctl & JOBCTL_PTRACE_FROZEN)) {
t->jobctl &= ~(JOBCTL_STOPPED | JOBCTL_TRACED);
state = TASK_WAKEKILL | __TASK_TRACED;
}
signal_wake_up_state(t, state);
}
对于signal_wake_up函数的解析,请看下回。