往process group 发送signal - linux kernel 实现浅欣赏

以前的浅析:
http://blog.csdn.net/leesagacious/article/details/53678666

一图胜千言,先看一幅图
这里写图片描述

/**
    kill(pid_t pid,int sig)

    kill 不是杀死进程,他是发信号
    可以向特定的进程组发送信号,

    好下面我们就看看linux是怎么实现的

    if (kill(-10,SIGUSR1) < 0){
        if (errno == ESRCH){  //目标进程或者进程组不存在
            perror("exit");
            exit(EXIT_FAILURE);
        }
        if (errno == EPERM){  //该进程没有权限向目标进程发送信号
        };

    }
*/
SYSCALL_DEFINE2(kill, pid_t, pid, int, sig)
{
    /*
        信号携带的一些信息,下面会为他赋值
        int si_signo; 
        int si_errno;
        int si_code;    

        怎么不kzalloc()了 ?  用栈上的内存了?  哈哈!
    */
    struct siginfo info;
    /*
        赋值初始化把

        si_signo:信号的值
        si_errno:产生信号的错误码,
        si_code: 信号的来源;
                SI_USER    :调用kill的用户进程,
                SI_ASYNCIO : 异步I/O操作完成后
                SI_TIMER   : POSIX定时器到期 
                SI_KERNEL  :内核产生的信号
    */
    info.si_signo = sig;
    info.si_errno = 0;
    info.si_code = SI_USER;
    info.si_pid = task_tgid_vnr(current); // sender pid
    info.si_uid = from_kuid_munged(current_user_ns(), current_uid()); //sender uid

    /*
        看pid 还是没有变化,还是我们从用户空间传递过来的
    */
    return kill_something_info(sig, &info, pid);
}
static int kill_something_info(int sig, struct siginfo *info, pid_t pid)
{
    int ret;
    /*
        看,用户空间传递过来的pid,就是在这里才发生化学反应
        就是pid > 0、 -1 之类的比较,决定将该信号按照你的意愿发送到哪里
        上图简单的画出了:
        > 0 : 向 pid 进程发
        =-1 : 发所有,init、自身是除外的,为什么?下面分析,
              所以,进程自己想用kill 自杀,在Linux Kernel中是不允许的,哈哈!
        < -1 : 看下面的具体实现
    */
    /*
        pid > 0 : 发送给进程 pid
        这里最先处理的是这一种情况,因为我们使用kill发送信号,大多数都是发送给一个特定的进程的,我们暂不分析这个,我们关注pid < -1 的情况
    */
    if (pid > 0) {
        .....
        return ret;
    }
    /*
        暂不分析
    */
    if (pid != -1) {
        .....
    } else {
        /*
            这个好狠呀,遍历系统中所有的进程
            task_struct 整个队列,它说要整个遍历一次,
            代价是很大的,
            <<Linux 内核设计与实现 第三版>>中描述:没有充足的理由别这样做 !
            作者在这里违反了这一条,不知道作者写到这儿的时候,有没有想起 这句话
        */
        for_each_process(p) {

            /*
                好,如果查找到了一个进程之后,看它是怎么做的

                task_pid_vnr(p) :  取得这个 进程的pid

                然后那么就 比较呗 !    
                看,是不能给自己进程发送信号的把
                same_thread_group ()
                {
                    return p1->signal == p2->signal;
                }   
            */
            if (task_pid_vnr(p) > 1 && !same_thread_group(p, current)) {
            /*
                看传递的参数,都是很熟悉的了吧

                    sig : 用户传递过来的sig
                    info: 一开始 栈上那块内存,携带一些信号的信息
                    p   : 通过上面两个函数校验后系统中的每一个进程

                    好,看它是怎么实现的
            */
            int err = group_send_sig_info(sig, info, p);
            }
        }
    }
}   
int group_send_sig_info(int sig, struct siginfo *info, struct task_struct *p)
{
    int ret;

    rcu_read_lock();
    /*
        发送信号的权限不正确  暂且不分析
    */
    ret = check_kill_permission(sig, info, p);
    rcu_read_unlock();

    /*
        发信号吧,但是这个函数不是最终发信号的,它包装了一些底层的函数,
        具体是挂入那个队列的事情了,看第 4 个参数,group 是 1,

        详细情况,你可以看我的另一篇博文了,哈哈!
        http://blog.csdn.net/leesagacious/article/details/53678666         
    */
    if (!ret && sig)
        ret = do_send_sig_info(sig, info, p, true);

    return ret;
}

猜你喜欢

转载自blog.csdn.net/leesagacious/article/details/65449894