当一个信号从内核或另一个进程发送到一个进程时,内核通过调用 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() …
发送信号
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); // 发送信号信息
}
kernel_siginfo
typedef struct kernel_siginfo {
__SIGINFO;
} kernel_siginfo_t;
sighand_struct
struct sighand_struct {
spinlock_t siglock;
refcount_t count;
wait_queue_head_t signalfd_wqh;
struct k_sigaction action[_NSIG];
};
pid_type
enum pid_type
{
PIDTYPE_PID,
PIDTYPE_TGID,
PIDTYPE_PGID,
PIDTYPE_SID,
PIDTYPE_MAX,
};
multiprocess_signals
struct multiprocess_signals {
sigset_t signal;
struct hlist_node node;
};
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);
send_signal_locked分析,继续往下看:
unlock_task_sighand(p, &flags); // 释放锁、恢复本地中断状态等
}
return ret;
}
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);
}
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))
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;
}
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
}