这里说下sys_kill函数的过程,流程图如下:
从上图中可以看出,sys_kill系统调用最终还是调用了group_send_sig_info,这个是线程组通知的函数,也就是信号进入公共信号队列,kill_pgrp_info就是递归某个进程组,然后给进程组中的每个线程组发送信号。
分析一下kill_something_info函数:
-
static int kill_something_info(int sig, struct siginfo *info, pid_t pid){
-
int ret;
-
-
if (pid > 0) { //当pid大于0的时候,发送给pid的那个进程
-
rcu_read_lock();
-
ret = kill_pid_info(sig, info, find_vpid(pid));
-
rcu_read_unlock();
-
return ret;
-
}
-
-
read_lock(&tasklist_lock);
-
if (pid != -1) { //如果不是-1的时候,也就是发送给进程组,如果0就是当前进程组,不是0也不是-1就是发送给-pid的进程组
-
ret = __kill_pgrp_info(sig, info,pid ? find_vpid(-pid) : task_pgrp(current));
-
} else { //发送给所有进程
-
int retval = 0, count = 0;
-
struct task_struct * p;
-
-
for_each_process(p) { //递归从init_task
-
if (task_pid_vnr(p) > 1 &&!same_thread_group(p, current)) {
-
int err = group_send_sig_info(sig, info, p);
-
++count;
-
if (err != -EPERM)
-
retval = err;
-
}
-
}
-
ret = count ? retval : -ESRCH;
-
}
-
read_unlock(&tasklist_lock);
-
return ret;
-
}
实际发送信号的过程就是在对应的进程的pending队列,写入信号的相关信息,这个过程在send_signal函数中,比较重要的函数就是prepare_signal函数和complete_signal函数,prepare_signal函数,在发送信号的过程中,如果这个进程收到了stop类的信号,那么就需要移除pending队列中的SIGCONT信号,如果收到了SIGCONT信号,那么group
stop过程就应该停止,移除pending队列中的stop类信号,同时让那个暂停的进程重新开始运行。
-
if (unlikely(signal->flags & SIGNAL_GROUP_EXIT)) { //处于group exit过程,参考 内核signal_struct中notify_count变量的作用
-
} else if (sig_kernel_stop(sig)) { //如果是stop类信号,移除SIGCONT信号
-
rm_from_queue(sigmask(SIGCONT), &signal->shared_pending);
-
t = p;
-
do {
-
rm_from_queue(sigmask(SIGCONT), &t->pending);
-
} while_each_thread(p, t);
-
} else if (sig == SIGCONT) { //如果是SIGCONT信号,移除stop信号,同时使那个进程重新开始执行
-
unsigned int why;
-
rm_from_queue(SIG_KERNEL_STOP_MASK, &signal->shared_pending);
-
t = p;
-
do {
-
unsigned int state;
-
rm_from_queue(SIG_KERNEL_STOP_MASK, &t->pending);
-
state = __TASK_STOPPED;
-
if (sig_user_defined(t, SIGCONT) && !sigismember(&t->blocked, SIGCONT)) {
-
set_tsk_thread_flag(t, TIF_SIGPENDING);
-
state |= TASK_INTERRUPTIBLE;
-
}
-
wake_up_state(t, state);
-
} while_each_thread(p, t);
对于那种SIGKILL,SIGSTOP这种信号,在调试器发送stop信号的时候,调用ptrace的attach操作的时候发送的信号是SEND_SIG_FORCED的,在处理信号的时候,在prepare_signal函数之后,会判断如果是SEND_SIG_FORCED,那么就不记录那个siginfo了。
complete_signal函数的作用就是找到线程组中的一个线程,唤醒他,设置TIF_SIGPENDING来处理信号。这个筛选线程的处理逻辑在want_signal中
-
static inline int wants_signal(int sig, struct task_struct *p)
-
{
-
if (sigismember(&p->blocked, sig)) //如果当前信号被block,就说明这个线程不适合处理信号
-
return 0;
-
if (p->flags & PF_EXITING) //如果当前进程正在退出,说明这个线程不能处理信号
-
return 0;
-
if (sig == SIGKILL) //如果是SIGKILL信号,就必须处理信号
-
return 1;
-
if (task_is_stopped_or_traced(p)) //如果当前进程处于暂停或者调试状态,说明这个线程不适合处理信号
-
return 0;
-
return task_curr(p) || !signal_pending(p); //如果是发送给当前线程的信号或者这个线程上没有信号需要处理,那么这个线程就应该处理这个信号
-
}
complete_signal的逻辑 kernel/signal.c
-
static void complete_signal(int sig, struct task_struct *p, int group)
-
{
-
struct signal_struct *signal = p->signal;
-
struct task_struct *t;
-
if (wants_signal(sig, p)) //判断目标线程是否可以发送信号
-
t = p;
-
else if (!group || thread_group_empty(p)) //如果目标线程不可以发送信号,在若这个信号是tkill过来的或者线程组中没有其他的线程,那么只能什么都不唤醒了。
-
return;
-
else { //递归线程组,找到一个合适的线程
-
t = signal->curr_target;
-
while (!wants_signal(sig, t)) {
-
t = next_thread(t);
-
if (t == signal->curr_target)
-
return;
-
}
-
signal->curr_target = t;
-
}
-
if (sig_fatal(p, sig) &&!(signal->flags & (SIGNAL_UNKILLABLE | SIGNAL_GROUP_EXIT)) &&!sigismember(&t->real_blocked, sig) &&
-
(sig == SIGKILL || !t->ptrace)) {
-
if (!sig_kernel_coredump(sig)) {
-
signal->flags = SIGNAL_GROUP_EXIT;
-
signal->group_exit_code = sig;
-
signal->group_stop_count = 0;
-
t = p;
-
do {
-
sigaddset(&t->pending.signal, SIGKILL);
-
signal_wake_up(t, 1);
-
} while_each_thread(p, t);
-
return;
-
}
-
}
-
signal_wake_up(t, sig == SIGKILL); //最后唤醒指定的线程,设置TIF_SIGPENDING
-
return;
-
}
这里进程调度的代码不熟悉,不多做分析,等看懂了进程调用的代码再来仔细分析。
总结:
kill信号发送给线程组,因为线程是共享主线程的signal结构的,发送给主线程的want_signal如果为false,那么是由那个线程组中其他的一个线程负责处理这个信号的,也就是说这种线程组group
kill,只需要线程组中有一个线程来处理信号即可,其他非线程组kill另当别论。