1 linux中的信号
Linux中的信号是用来通知进程发生了异步事件。在原理上,一个进程收到一个信号与一个处理器收到一个中断请可以看成一致的。信号是linux进程间通信机制中的唯一的异步通信机制,一个进程不必通过任何操作来等待信号的到达,事实上,进程也无法提前感知信号什么时候到达由谁发送。当然内核也会因为内部事件给进程发送信号。信号机制除了基本通知功能外,还可以传递附加信息。
常用的信号发送函数有raise kill等 常用的信号注册函数有signal、sigaction等。
2 源码分析
2.1 信号发送
此处拿raise函数分析
raise函数实际调用的是tgkill系统调用,tgkill系统调用实际上是调用的do_tkill函数
3289 static int do_tkill(pid_t tgid, pid_t pid, int sig)
3290 {
3291 struct siginfo info;
3292
3293 clear_siginfo(&info);
3294 info.si_signo = sig;
3295 info.si_errno = 0;
3296 info.si_code = SI_TKILL;
3297 info.si_pid = task_tgid_vnr(current);
3298 info.si_uid = from_kuid_munged(current_user_ns(), current_uid());
3299
3300 return do_send_specific(tgid, pid, sig, &info);
3301 }
上述代码的3293-3298行可以看到初始化了一个info数据结构,其中3294是保存sig信号值,3295是设置错误码,3296行设置是发送的此信号的途径,这里是由tkill发送,3297-3298行是设置pid和uid。3300行可以看到接着调用do_send_specifi。
do_send_specific(pid_t tgid, pid_t pid, int sig, struct siginfo *info)
3261 {
3262 struct task_struct *p;
3263 int error = -ESRCH;
3264
3265 rcu_read_lock();
3266 p = find_task_by_vpid(pid);
3267 if (p && (tgid <= 0 || task_tgid_vnr(p) == tgid)) {
3268 error = check_kill_permission(sig, info, p);
3269 /*
3270 * The null signal is a permissions and process existence
3271 * probe. No signal is actually delivered.
3272 */
3273 if (!error && sig) {
3274 error = do_send_sig_info(sig, info, p, false);
3275 /*
3276 * If lock_task_sighand() failed we pretend the task
3277 * dies after receiving the signal. The window is tiny,
3278 * and the signal is private anyway.
3279 */
3280 if (unlikely(error == -ESRCH))
3281 error = 0;
3282 }
3283 }
3284 rcu_read_unlock();
3285
3286 return error;
3287 }
上述代码3266行可以看到根据传入的pid获取目标进程的进程描述符p,如果3268行传入的参数允许则error肯定是0,且传入的sig肯定大于0(之前的函数会对传入参数做出检查),则3273条件在此次调用上下文肯定成立,则会执行3274行执行信号传递。
1163 int do_send_sig_info(int sig, struct siginfo *info, struct task_struct *p,
1164 bool group)
1165 {
1166 unsigned long flags;
1167 int ret = -ESRCH;
1168
1169 if (lock_task_sighand(p, &flags)) {
1170 ret = send_signal(sig, info, p, group);
1171 unlock_task_sighand(p, &flags);
1172 }
1173
1174 return ret;
1175 }
上述代码可以看到do_send_sig_info函数调用的是send_signal函数,接着send_signal函数(这里的实现代码不展开了)会继续调用__send_signal函数,此处对__send_signal主要代码进行展示
1000 static int __send_signal(int sig, struct siginfo *info, struct task_struct *t,
1001 int group, int from_ancestor_ns)
...
1015 pending = group ? &t->signal->shared_pending : &t->pending;
...
1042 if (sig < SIGRTMIN)
1043 override_rlimit = (is_si_special(info) || info->si_code >= 0);
1044 else
1045 override_rlimit = 0;
1046
1047 q = __sigqueue_alloc(sig, t, GFP_ATOMIC, override_rlimit);
1048 if (q) {
1049 list_add_tail(&q->list, &pending->list);
1050 switch ((unsigned long) info) {
1051 case (unsigned long) SEND_SIG_NOINFO:
1052 clear_siginfo(&q->info);
1053 q->info.si_signo = sig;
1054 q->info.si_errno = 0;
1055 q->info.si_code = SI_USER;
1056 q->info.si_pid = task_tgid_nr_ns(current,
1057 task_active_pid_ns(t));
1058 q->info.si_uid = from_kuid_munged(current_user_ns(), current_uid());
1059 break;
1060 case (unsigned long) SEND_SIG_PRIV:
1061 clear_siginfo(&q->info);
1062 q->info.si_signo = sig;
1063 q->info.si_errno = 0;
1064 q->info.si_code = SI_KERNEL;
1065 q->info.si_pid = 0;
1066 q->info.si_uid = 0;
1067 break;
1068 default:
1069 copy_siginfo(&q->info, info);
1070 if (from_ancestor_ns)
1071 q->info.si_pid = 0;
1072 break;
1073 }
1074
1075 userns_fixup_signal_uid(&q->info, t);
1076
1077 } else if (!is_si_special(info)) {
1078 if (sig >= SIGRTMIN && info->si_code != SI_USER) {
1079 /*
1080 * Queue overflow, abort. We may abort if the
1081 * signal was rt and sent by user using something
1082 * other than kill().
1083 */
1084 result = TRACE_SIGNAL_OVERFLOW_FAIL;
1085 ret = -EAGAIN;
1086 goto ret;
1087 } else {
1088 /*
1089 * This is a silent loss of information. We still
1090 * send the signal, but the *info bits are lost.
1091 */
1092 result = TRACE_SIGNAL_LOSE_INFO;
1093 }
1094 }
1095
1096 out_set:
1097 signalfd_notify(t, sig);
1098 sigaddset(&pending->signal, sig);
1099 complete_signal(sig, t, group);
1100 ret:
1101 trace_signal_generate(sig, info, t, group, result);
1102 return ret;
1103 }
对于上述代码此次调用上下文由于传入的group是false则1015行肯定是t->pending的地址也就是共享信号数据结构(在下一节信号响应可以看到共享与不共享的区别)。1047行分配一个sigqueue结构体,在此次调用上下文我们传入的info数据结构有效则结合1050-1073行代码可以确定执行到1068-1071行保存传入的info数据结构,在1098行则是调用sigaddset函数去设置对应的函数标志位记录有信号递送过来。1099行的函数整体complete_signal会调用signal_wake_up函数会间接调用 set_tsk_thread_flag去设置目标进程的TIF_SIGPENDING标志。其中对于非排队信号处理是在上述1022-1023行处理的,legacy_queue函数发现信号值是非排队信号以及pengding中已经存在了信号位则直接跳转到ret标签处执行返回。 函数执行流程图如下图。
2.2 信号处理函数注册
先看下signal函数对应的系统调用如下
3889 SYSCALL_DEFINE2(signal, int, sig, __sighandler_t, handler)
3890 {
3891 struct k_sigaction new_sa, old_sa;
3892 int ret;
3893
3894 new_sa.sa.sa_handler = handler;
3895 new_sa.sa.sa_flags = SA_ONESHOT | SA_NOMASK;
3896 sigemptyset(&new_sa.sa.sa_mask);
3897
3898 ret = do_sigaction(sig, &new_sa, &old_sa);
3899
3900 return ret ? ret : (unsigned long)old_sa.sa.sa_handler;
3901 }
上述代码3839行则是定义两个sigaction变量new_sa\old_sa。3894-3895初始化了新定义的变量new_sa,将用户态的处理函数对应的虚拟地址保存在handler中,并且清空new_sa里sa.sa_mask。然后调用do_sigaction
3452 int do_sigaction(int sig, struct k_sigaction *act, struct k_sigaction *oact)
3453 {
3454 struct task_struct *p = current, *t;
3455 struct k_sigaction *k;
3456 sigset_t mask;
3457
3458 if (!valid_signal(sig) || sig < 1 || (act && sig_kernel_only(sig)))
3459 return -EINVAL;
3460
3461 k = &p->sighand->action[sig-1];
3462
3463 spin_lock_irq(&p->sighand->siglock);
3464 if (oact)
3465 *oact = *k;
3466
3467 sigaction_compat_abi(act, oact);
3468
3469 if (act) {
3470 sigdelsetmask(&act->sa.sa_mask,
3471 sigmask(SIGKILL) | sigmask(SIGSTOP));
3472 *k = *act;
3473 /*
3474 * POSIX 3.3.1.3:
3475 * "Setting a signal action to SIG_IGN for a signal that is
3476 * pending shall cause the pending signal to be discarded,
3477 * whether or not it is blocked."
3478 *
3479 * "Setting a signal action to SIG_DFL for a signal that is
3480 * pending and whose default action is to ignore the signal
3481 * (for example, SIGCHLD), shall cause the pending signal to
3482 * be discarded, whether or not it is blocked"
3483 */
3484 if (sig_handler_ignored(sig_handler(p, sig), sig)) {
3485 sigemptyset(&mask);
3486 sigaddset(&mask, sig);
3487 flush_sigqueue_mask(&mask, &p->signal->shared_pending);
3488 for_each_thread(p, t)
3489 flush_sigqueue_mask(&mask, &t->pending);
3490 }
3491 }
3492
3493 spin_unlock_irq(&p->sighand->siglock);
3494 return 0;
3495 }
上述代码3454-3460行是变量定义以及参数检查,第3461行是获取对应的k_sigaction保存handler的内存地址,第3464-3465将旧的k_sigaction保存到传入参数oact中,第3469行判断传入的act非空,则进入if语句,3470行则将新的act中的sigmask里的SIGKILL和SIGKILL删除,3472行将新传入的act保存到p->sighand->action[sig-1]变量中。3484-3490行对SIG_IGN和SIG_DFL情况做处理这里不展开说明,感兴趣可以去看看源码’kernel/signal.c’。
函数流程如下图
2.3 信号响应
信号处理是在退出异常处理程序或者中断处理程序返回用户态之前会去检查是否存在信号需要处理,可以再ret_from_exception汇编代码段看到调用prepare_exit_to_usermode函数后续代码我就不贴了可以结合下面流程图看代码:
在信号执行过程中内核态和用户态之间转换图如下: