• send_sig: 内核执行流程


    当一个信号从内核或另一个进程发送到一个进程时,内核通过调用 send_sig()、send_sig_info()、force_sig() 或 force_sig_info() 函数来传递它。
    调用关系如下:
      send_sig() -> send_sig_info() … send_signal_locked() …
      force_sig() -> force_sig_info() … send_signal_locked() …

    目录


    1. 源码流程

    1.1 send_sig

    2. 源码结构

    3. 部分结构定义

    4. 扩展函数


    内容


    1. 源码流程

    1.1 send_sig

      发送信号

    int     
    send_sig(int sig, struct task_struct *p, int priv)
    {       
            return send_sig_info(sig, __si_special(priv), p);
    }
    ||
    \/
    int send_sig_info(int sig, struct kernel_siginfo *info, struct task_struct *p)
    {
            /* 确保遗留内核用户不发送错误值(正常路径在 check_kill_permission 中检查)*/
            if (!valid_signal(sig))
            // return sig <= _NSIG ? 1 : 0;
            // #define _NSIG           64
                    return -EINVAL;
    
            return do_send_sig_info(sig, info, p, PIDTYPE_PID); // 发送信号信息
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

      do_send_sig_info分析


    2. 源码结构


    3. 部分结构定义

      kernel_siginfo

    typedef struct kernel_siginfo {
    	__SIGINFO;
    } kernel_siginfo_t;
    
    • 1
    • 2
    • 3

      sighand_struct

    struct sighand_struct {
    	spinlock_t		siglock;
    	refcount_t		count;
    	wait_queue_head_t	signalfd_wqh;
    	struct k_sigaction	action[_NSIG];
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

      pid_type

    enum pid_type
    {
            PIDTYPE_PID,    
            PIDTYPE_TGID,           
            PIDTYPE_PGID,   
            PIDTYPE_SID,            
            PIDTYPE_MAX,
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

      multiprocess_signals

    struct multiprocess_signals {
    	sigset_t signal;
    	struct hlist_node node;
    };
    
    • 1
    • 2
    • 3
    • 4

    4. 扩展函数

    do_send_sig_info

    int do_send_sig_info(int sig, struct kernel_siginfo *info, struct task_struct *p,
                            enum pid_type type)
    {
            unsigned long flags;
            int ret = -ESRCH;
    
            if (lock_task_sighand(p, &flags)) { // 获取tsk->sighand,并且获取自旋锁(保存本地中断状态,关闭本地中断)
                    ret = send_signal_locked(sig, info, p, type);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

      send_signal_locked分析,继续往下看:

     				unlock_task_sighand(p, &flags); // 释放锁、恢复本地中断状态等
            }
    
            return ret;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    send_signal_locked

    int send_signal_locked(int sig, struct kernel_siginfo *info,
                           struct task_struct *t, enum pid_type type)
    {
            /* pid namespace init 是否应该接收 SIGKILL 或 SIGSTOP? */
            bool force = false;
    
            if (info == SEND_SIG_NOINFO) {
            // #define SEND_SIG_NOINFO ((struct kernel_siginfo *) 0)
                    /* 如果从ancestor pid命名空间发送,则强制 */
                    force = !task_pid_nr_ns(current, task_active_pid_ns(t));
            } else if (info == SEND_SIG_PRIV) {
            #define SEND_SIG_PRIV   ((struct kernel_siginfo *) 1)
                    /* 不要忽略内核生成的信号 */
                    force = true;
            } else if (has_si_pid_and_uid(info)) { // SIL_KILL 、SIL_CHLD 、SIL_RT
                    /* SIGKILL和SIGSTOP是特殊的或有id */
                    struct user_namespace *t_user_ns;
    
                    rcu_read_lock();
                    t_user_ns = task_cred_xxx(t, user_ns); // t->real_cred->user_ns
    				if (current_user_ns() != t_user_ns) {
                            kuid_t uid = make_kuid(current_user_ns(), info->si_uid); // 将用户名称空间uid 对映射到kuid
                            info->si_uid = from_kuid_munged(t_user_ns, uid); // 从kuid用户名称空间创建一个uid
                    }
                    rcu_read_unlock();
    
    				/* 内核生成的信号? */
                    force = (info->si_code == SI_KERNEL);
    
                    /* 来自ancestor pid命名空间? */
                    if (!task_pid_nr_ns(current, task_active_pid_ns(t))) {
                            info->si_pid = 0;
                            force = true;
                    }
            }
            return __send_signal_locked(sig, info, t, type, force);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

      __send_signal_locked分析


    __send_signal_locked

    static int __send_signal_locked(int sig, struct kernel_siginfo *info,
                                    struct task_struct *t, enum pid_type type, bool force)
    {
            struct sigpending *pending;
            struct sigqueue *q;
            int override_rlimit;
            int ret = 0, result;
    
            lockdep_assert_held(&t->sighand->siglock); // 锁调试为真 并且 siglock为空
    
            result = TRACE_SIGNAL_IGNORED;
            if (!prepare_signal(sig, t, force))
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

      prepare_signal分析,继续往下看:

    pending = (type != PIDTYPE_PID) ? &t->signal->shared_pending : &t->pending;
            
    /* 短路忽略信号,支持精确排队一个非rt信号,让我们可以得到更详细的信号成因信息 */
    result = TRACE_SIGNAL_ALREADY_PENDING;
    if (legacy_queue(pending, sig)) // 如果是不可靠(非实时)信号 并且 已挂起到信号队列
    // #define SIGRTMIN        32
            goto ret;
    
    result = TRACE_SIGNAL_DELIVERED;
    /* 跳过 SIGKILL 和内核线程的无用 siginfo 分配 */
    if ((sig == SIGKILL) || (t->flags & PF_KTHREAD))
           goto out_set;
    
    /* 如果通过 sigqueue 或其他一些实时机制发送实时信号,则必须排队
     kill() 是否这样做是由实现定义的
     我们尝试这样做,本着最小意外的原则,
     但是由于在内存不足时不允许 kill 以 EAGAIN 失败,
     我们只需确保至少传递一个信号并且不传递信息结构 */
    if (sig < SIGRTMIN)
                    override_rlimit = (is_si_special(info) || info->si_code >= 0);
            else
                    override_rlimit = 0;
    
    		/* 分配一个新的信号队列记录 - 当且仅当 t == current 时才可以在没有锁的情况下调用它,
    		否则必须持有适当的锁以阻止目标任务退出 */
            q = __sigqueue_alloc(sig, t, GFP_ATOMIC, override_rlimit, 0);
    
    		if (q) {
                    list_add_tail(&q->list, &pending->list);
                    switch ((unsigned long) info) {
                    case (unsigned long) SEND_SIG_NOINFO:
                            clear_siginfo(&q->info);
                            q->info.si_signo = sig;
                            q->info.si_errno = 0;
                            q->info.si_code = SI_USER;
                            q->info.si_pid = task_tgid_nr_ns(current,
                                                            task_active_pid_ns(t));
                            rcu_read_lock();
                            q->info.si_uid =
                                    from_kuid_munged(task_cred_xxx(t, user_ns),
                                                     current_uid());
                            rcu_read_unlock();
                            break;
    				case (unsigned long) SEND_SIG_PRIV:
                            clear_siginfo(&q->info);
                            q->info.si_signo = sig;
                            q->info.si_errno = 0;
                            q->info.si_code = SI_KERNEL;
                            q->info.si_pid = 0;
                            q->info.si_uid = 0;
                            break;
                   	
                   	...
                   	/* 这是一种无声的信息丢失
                   	我们仍然发送信号,但 *info 位丢失了 */
                   	result = TRACE_SIGNAL_LOSE_INFO;
    		}
    
    out_set:
    		/* 将信号传递给监听signalfd */
            signalfd_notify(t, sig); 
            // 如果t进程的信号等待列表不为空
            // 唤醒阻塞在t进程的信号等待队列
            
            sigaddset(&pending->signal, sig); // sig放入信号数组
            // set->sig
    
    		/* 让多进程信号出现在正在进行的fork之后 */
    		if (type > PIDTYPE_TGID) {
                    struct multiprocess_signals *delayed;
                    hlist_for_each_entry(delayed, &t->signal->multiprocess, node) {
                            sigset_t *signal = &delayed->signal;
                            /* Can't queue both a stop and a continue signal */
                            if (sig == SIGCONT) // #define SIGCONT         18
                                    sigdelsetmask(signal, SIG_KERNEL_STOP_MASK); // 删除停止标志
                            else if (sig_kernel_stop(sig)) // 是否需要停止
                            // 如果是不可靠(非实时)信号,
                            // 并且含有SIGSTOP、SIGTSTP、SIGTTIN、SIGTTOU其中一种
                                    sigdelset(signal, SIGCONT); //  sig放入 SIGCONT
                            sigaddset(signal, sig);
                    }
            }
    
    		complete_signal(sig, t, type); // 用于之后快速检测是否有未处理的信号
    		// 调用signal_wake_up
    		// 线程TIF_SIGPENDING标志设为1
    
    ret:
            trace_signal_generate(sig, info, t, type != PIDTYPE_PID, result); // tracepoint函数
            return ret;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91

    prepare_signal

    static bool prepare_signal(int sig, struct task_struct *p, bool force)
    {
            struct signal_struct *signal = p->signal;
            struct task_struct *t;
            sigset_t flush;
    
            if (signal->flags & SIGNAL_GROUP_EXIT) {
            // #define SIGNAL_GROUP_EXIT       0x00000004 正在进行组退出
                    if (signal->core_state)
                            return sig == SIGKILL;
                    /*
                     * 进程处于濒死状态,无事可做
                     */
            } else if (sig_kernel_stop(sig)) {
                    /*
                     * 这是一个停止信号,从所有队列中删除 SIGCONT
                     */
                    siginitset(&flush, sigmask(SIGCONT)); // 初始化sig
                    flush_sigqueue_mask(&flush, &signal->shared_pending); // 从待处理的集合和队列中删除掩码中的信号
                    for_each_thread(p, t)
                            flush_sigqueue_mask(&flush, &t->pending);
            } else if (sig == SIGCONT) {
    				unsigned int why;
                    /*
                     * 从所有队列中移除所有停止信号,唤醒所有线程
                     */
                    siginitset(&flush, SIG_KERNEL_STOP_MASK);
                    flush_sigqueue_mask(&flush, &signal->shared_pending);
                    for_each_thread(p, t) {
                            flush_sigqueue_mask(&flush, &t->pending);
                            task_clear_jobctl_pending(t, JOBCTL_STOP_PENDING); // jobctl 清除JOBCTL_STOP_PENDING标志
                            if (likely(!(t->ptrace & PT_SEIZED))) {
                                    t->jobctl &= ~JOBCTL_STOPPED; // 清除JOBCTL_STOPPED标志
                                    wake_up_state(t, __TASK_STOPPED); // 唤醒一个线程
                            } else
                            		/* 此函数安排粘性 ptrace 陷阱,
                            		该陷阱在下一个 TRAP_STOP 时清除,
                            		以通知 ptracer 事件 */
                                    ptrace_trap_notify(t); // 安排陷阱通知ptracer
                    }
                    ...
                    return !sig_ignored(p, sig, force); // 忽略信号 或下面四种状态为真
                    // SIGCONT、SIGCHLD、SIGWINCH、SIGURG
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
  • 相关阅读:
    JVM常见参数总结
    海南热带海洋学院金秋悦读《乡村振兴战略下传统村落文化旅游设计》2023新学年许少辉八一新书​
    基于Java的城市天然气费管理系统的设计与实现(源码+lw+部署文档+讲解等)
    Redis-哨兵模式
    【Java】安装JDK开发者工具包并编写第一个程序“Hello World.java”
    C++学习笔记(二十三)
    NodeMCU ESP8266 基于Arduino IDE的串口图形化调试教程(超详细)
    【LIUNX】查询IP与MAC地址
    基于VUE + Echarts 实现可视化数据大屏农村信息可视化
    30.10.4 角色管理
  • 原文地址:https://blog.csdn.net/a29562268/article/details/126236089