分类: LINUX
2013-03-12 15:09:39
分类: C/C++
原文地址: 作者:
kill信号涉及到的系统调用有sys_kill,sys_tkill,sys_tgkill函数等,sys_kill的处理的机制有以下这么几种:
pid=0信号被发送到当前调用sys_kill操作的进程组上。
pid=-1信号被发送给所有进程。
这里说下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另当别论。