不论是内核发送信号(硬件异常-SIGSEGV等、软件通知-SIGPIPE等、终端键-SIGINT等),还是用户进程发送信号(kill系统调用),都要由内核将信号记录到相应(轻量级)进程描述符中的信号相关结构中、唤醒被阻塞的目标进程等。
在信号发送阶段,内核将信号添加到信号pending队列中;在信号传递阶段,内核将信号从pending队列中取出,并处理(包括调用用户自定义处理、SIG_DFL默认处理、SIG_IGN忽略处理)。
注:linux内核无线程概念,线程的功能是由轻量级进程实现,以下线程均代表轻量级进程;线程组代表进程,包括只有主控线程的进程
I.signal发送函数
信号可以发送到线程、线程组、进程组
内核主要通过以下函数发送信号:
线程
sys_tkill/sys_tgkill:tkill/tgkill系统调用对应的内核服务
send_sig:发送信号到线程
force_sig:强制发送信号到线程;当目标进程忽略该信号时,将信号处理重置为SIG_DFL;当目标进程阻塞该信号时,将信号处理重置为SIG_DFL并清空阻塞mask中对应的信号位
线程组(进程)
sys_kill:kill系统调用对应的内核服务;当参数pid大于0时,会发送信号给线程组
group_send_sig_info:发送信号给某线程组
进程组
__kill_pgrp_info:发送信号给进程组内的所有线程组
以上函数的调用关系图如下:
信号发送到线程和线程组的区别主要是,将信号pending到私有信号pending队列还是共享信号pending队列;发送信号到进程组其实就是发送信号到进程组内的所有线程组
II.发送函数实现
由上图可知,所有的函数都会最终调用send_signal实现信号的发送
i.send_signal
/* kernel/signal.c */
916 static int send_signal(int sig, struct siginfo *info, struct task_struct *t,
917 int group)
918 {
919 int from_ancestor_ns = 0;
920
921 #ifdef CONFIG_PID_NS
922 if (!is_si_special(info) && SI_FROMUSER(info) &&
923 task_pid_nr_ns(current, task_active_pid_ns(t)) <= 0)
924 from_ancestor_ns = 1;
925 #endif
926
927 return __send_signal(sig, info, t, group, from_ancestor_ns);
928 }
from_ancestor_ns置位必须满足以下所有条件:
1.siginfo不是特殊的信号信息,SEND_SIG_NOINFO,SEND_SIG_PRIV,SEND_SIG_FORCED
2.信号是从用户进程通过kill发出的
3.发送信号的进程current是上级pid命名空间的进程(即current不在下级pid命名空间中),接收方为下级pid命名空间的进程;由alloc_pid可以看出,下级pid命名空间分配进程id时,也会在所有上级分配一个id;比如有两级pid命名空间,上级的id有1、2...1023,下级id有1、2,当下级创建进程则会在下级分配3上级分配1024,即上级的1024与下级的3是同一进程,此时上级空间的2进程向下级空间2发送信号就叫from_ancestor_ns
注:由struct pid中numbers数组大小为1及alloc_pid处理可知目前内核版本(2.6.32.60)只支持一级pid命名空间,所以from_ancestor_ns恒等于0
send_signal会以from_ancestor_ns=0调用__send_signal
832 static int __send_signal(int sig, struct siginfo *info, struct task_struct *t,
833 int group, int from_ancestor_ns)
834 {
835 struct sigpending *pending;
836 struct sigqueue *q;
837 int override_rlimit;
838
839 trace_sched_signal_send(sig, t);
840
841 assert_spin_locked(&t->sighand->siglock);
842
843 if (!prepare_signal(sig, t, from_ancestor_ns))
844 return 0;
845
846 pending = group ? &t->signal->shared_pending : &t->pending;
847 /*
848 * Short-circuit ignored signals and support queuing
849 * exactly one non-rt signal, so that we can get more
850 * detailed information about the cause of the signal.
851 */
852 if (legacy_queue(pending, sig))
853 return 0;
854 /*
855 * fast-pathed signals for kernel-internal things like SIGSTOP
856 * or SIGKILL.
857 */
858 if (info == SEND_SIG_FORCED)
859 goto out_set;
860
861