Linux内核进程管理子系统有什么第二十六回 —— 进程主结构详解(22)
接前一篇文章: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函数的解析,请看下回。