- s4 异常分析
1.信号响应时机
对一个进程发送一个信号以后,其实并没有硬中断发生,只是简单把信号挂载到目标进程的信号 pending 队列上去,信号真正得到执行的时机是进程执行完异常/中断返回到用户态的时刻。
让信号看起来是一个异步中断的关键就是,正常的用户进程是会频繁的在用户态和内核态之间切换(这种切换包括:系统调用、缺页异常、系统中断…),所以信号能很快的能得到执行。但这也带来了一点问题,内核进程是不响应信号的,除非它刻意的去查询。所以通常情况下无法通过kill命令去杀死一个内核进程。
2.信号的响应
内核在每次跳转到用户空间之前,都会检查一下当前这个 task 的标志状态,如果 TIF_SIGPENDING 标志被置位,那么就说明当前这个 task 有信号需要处理,内核就会开始进行信号的响应处理。下面是内核跳转到用户空间前的汇编代码 ret_from_exception:
arch/mips/kernel/entry.s:
30 #ifndef CONFIG_PREEMPT
31 FEXPORT(ret_from_exception)
32 local_irq_disable # preempt stop
33 b __ret_from_irq
34 #endif
37 FEXPORT(__ret_from_irq)
42 resume_userspace_check:
43 LONG_L t0, PT_STATUS(sp) # returning to kernel mode?
44 andi t0, t0, KU_USER
45 beqz t0, resume_kernel
46
47 __cond_branch_hazard
48 resume_userspace:
49 local_irq_disable # make sure we dont miss an
50 # interrupt setting need_resched
51 # between sampling and return
52 LONG_L a2, TI_FLAGS($28) # current->work
53 andi t0, a2, _TIF_WORK_MASK # (ignoring syscall_trace)
54 bnez t0, work_pending
55 j restore_all
57 #ifdef CONFIG_PREEMPT
58 __cond_branch_hazard
59 resume_kernel:
60 local_irq_disable
61 lw t0, TI_PRE_COUNT($28)
62 bnez t0, restore_all
63 need_resched:
64 LONG_L t0, TI_FLAGS($28)
65 andi t1, t0, _TIF_NEED_RESCHED
66 beqz t1, restore_all
67 LONG_L t0, PT_STATUS(sp) # Interrupts off?
68 andi t0, 1
69 beqz t0, restore_all
70 jal preempt_schedule_irq
71 b need_resched
72 #endif
128 work_pending:
129 andi t0, a2, _TIF_NEED_RESCHED # a2 is preloaded with TI_FLAGS
130 beqz t0, work_notifysig
131 __cond_branch_hazard
147 work_notifysig: # deal with pending signals and
148 # notify-resume requests
149 move a0, sp
150 li a1, 0
151 jal do_notify_resume # a2 already loaded
152 j resume_userspace_check
- 第53行:andi t0, a2, _TIF_WORK_MASK # (ignoring syscall_trace)
148 /* work to do on interrupt/exception return */
149 #define _TIF_WORK_MASK \
150 (_TIF_SIGPENDING | _TIF_NEED_RESCHED | _TIF_NOTIFY_RESUME | \
151 _TIF_UPROBE)
这条指令判断当前的 task 判断标志,如果有需要处理的标志,那么就跳转到 work_pending,在这里判断如果有收到信号,那么就调用 do_signal 进行信号的响应。
arch/mips/kernel/signal.c:
–> do_notify_resume() -> do_signal() -> get_signal()/handle_signal()
878 asmlinkage void do_notify_resume(struct pt_regs *regs, void *unused,
879 __u32 thread_info_flags)
880 {
881 local_irq_enable();
882
883 user_exit();
884
885 if (thread_info_flags & _TIF_UPROBE)
886 uprobe_notify_resume(regs);
887
888 /* deal with pending signal delivery */
889 if (thread_info_flags & _TIF_SIGPENDING)
890 do_signal(regs);
891
892 if (thread_info_flags & _TIF_NOTIFY_RESUME) {
893 clear_thread_flag(TIF_NOTIFY_RESUME);
894 tracehook_notify_resume(regs);
895 rseq_handle_notify_resume(NULL, regs);
896 }
897
898 user_enter();
899 }
refer to
- https://wushifublog.com/2020/05/16/%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3Linux%E5%86%85%E6%A0%B8%E2%80%94%E2%80%94signals/
- http://lzz5235.github.io/2015/06/04/signal.html