kill信号涉及到的系统调用有sys_kill,sys_tkill,sys_tgkill函数等,sys_kill的处理的机制有以下这么几种:
- pid>0信号被发送到pid的那个进程上。
- pid=0
-
pid=-1信号被发送给所有进程。
- pid<-1信号被发送到-pid的线程组上。
这里说下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另当别论。