Chinaunix首页 | 论坛 | 博客
  • 博客访问: 318776
  • 博文数量: 49
  • 博客积分: 4045
  • 博客等级: 上校
  • 技术积分: 1195
  • 用 户 组: 普通用户
  • 注册时间: 2007-06-15 12:56
文章分类

全部博文(49)

文章存档

2013年(29)

2010年(6)

2009年(6)

2008年(8)

我的朋友

分类: LINUX

2013-03-12 15:09:39

2013-03-07 16:51:06

分类: C/C++

原文地址: 作者:


kill信号涉及到的系统调用有sys_kill,sys_tkillsys_tgkill函数等,sys_kill的处理的机制有以下这么几种:

  1. pid>0信号被发送到pid的那个进程上。
  2. pid=0信号被发送到当前调用sys_kill操作的进程组上。

  3. pid=-1信号被发送给所有进程。

  4. 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);


     对于那种SIGKILLSIGSTOP这种信号,在调试器发送stop信号的时候,调用ptraceattach操作的时候发送的信号是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另当别论。

阅读(547) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~