// 本文部分内容来自网络
// 基于内核版本3.4
一.整体流程
Linux 用户进程不能处理信号,操作系统也不会为了处理一个信号而把当前正在运行的进程挂起,而是选择在内核态切换回用户态的时候(一般都是中断或者系统调用返回)处理信号。
所以处理信号的整个过程是这样的:进程由于 系统调用或者中断 进入内核,完成相应任务返回用户空间的前夕,检查信号队列,如果有信号,则根据信号向量表找到信号处理函数,设置好“堆栈”后,跳到用户态执行信号处理函数。信号处理函数执行完毕后,返回内核态,设置“堆栈”,再返回到用户态继续执行程序。
具体步骤:
1、用户为某信号注册一个信号处理函数sighandler
。
2、当前正在执行主程序,这时候因为中断、异常或系统调用进入内核态。
3、在处理完异常要返回用户态的主程序之前,检查到有信号未处理,并发现该信号需要按照用户自定义的函数来处理。
4、内核决定返回用户态执行sighandler
函数,而不是恢复main
函数的上下文继续执行!(sighandler
和main
函数使用的是不同的堆栈空间,它们之间不存在调用和被调用的关系,是两个独立的控制流程)
5、sighandler
函数返回后,执行特殊的系统调用sigreturn
从用户态回到内核态
6、检查是否还有其它信号需要递达,如果没有 则返回用户态并恢复主程序的上下文信息继续执行。
二.代码分析
ARM Linux中,内核态返回用户态通常接口是ret_fast_syscall(系统调用)和ret_to_user(中断),所以signal的处理流程:ret_to_user()/ret_fast_syscall–>work_pending()–>do_notify_resume()–>do_signal
以ret_fast_syscall为例,分析具体实现,代码位于 archarmkernelentry-common.S
ret_fast_syscall: UNWIND(.fnstart ) UNWIND(.cantunwind ) disable_irq @ disable interrupts ldr r1, [tsk, #TI_FLAGS] tst r1, #_TIF_WORK_MASK bne fast_work_pending #if defined(CONFIG_IRQSOFF_TRACER) asm_trace_hardirqs_on #endif /* perform architecture specific actions before user return */ arch_ret_to_user r1, lr restore_user_regs fast = 1, offset = S_OFF UNWIND(.fnend )
ldr r1, [tsk, #TI_FLAGS]:
TI_FLAGS表示thread_info结构体中的flags变量,相关宏定义位于archarmkernelAsm-offsets.c
DEFINE(TI_FLAGS, offsetof(struct thread_info, flags)); DEFINE(TI_PREEMPT, offsetof(struct thread_info, preempt_count)); DEFINE(TI_ADDR_LIMIT, offsetof(struct thread_info, addr_limit)); DEFINE(TI_TASK, offsetof(struct thread_info, task)); DEFINE(TI_EXEC_DOMAIN, offsetof(struct thread_info, exec_domain)); DEFINE(TI_CPU, offsetof(struct thread_info, cpu)); DEFINE(TI_CPU_DOMAIN, offsetof(struct thread_info, cpu_domain)); DEFINE(TI_CPU_SAVE, offsetof(struct thread_info, cpu_context)); DEFINE(TI_USED_CP, offsetof(struct thread_info, used_cp)); DEFINE(TI_TP_VALUE, offsetof(struct thread_info, tp_value)); DEFINE(TI_FPSTATE, offsetof(struct thread_info, fpstate)); DEFINE(TI_VFPSTATE, offsetof(struct thread_info, vfpstate));
flags变量表示了该线程基本状态,例如是否有信号pending,是否需要调度等,其可能的值定义位于/arch/arm/include/asm/thread_info.h:
/* * thread information flags: * TIF_SYSCALL_TRACE - syscall trace active * TIF_SYSCAL_AUDIT - syscall auditing active * TIF_SIGPENDING - signal pending * TIF_NEED_RESCHED - rescheduling necessary * TIF_NOTIFY_RESUME - callback before returning to user * TIF_USEDFPU - FPU was used by this task this quantum (SMP) * TIF_POLLING_NRFLAG - true if poll_idle() is polling TIF_NEED_RESCHED */ #define TIF_SIGPENDING 0 #define TIF_NEED_RESCHED 1 #define TIF_NOTIFY_RESUME 2 /* callback before returning to user */ #define TIF_SYSCALL_TRACE 8 #define TIF_SYSCALL_AUDIT 9 #define TIF_POLLING_NRFLAG 16 #define TIF_USING_IWMMXT 17 #define TIF_MEMDIE 18 /* is terminating due to OOM killer */ #define TIF_RESTORE_SIGMASK 20 #define TIF_SECCOMP 21 #define TIF_SWITCH_MM 22 /* deferred switch_mm */ #define _TIF_SIGPENDING (1 << TIF_SIGPENDING) #define _TIF_NEED_RESCHED (1 << TIF_NEED_RESCHED) #define _TIF_NOTIFY_RESUME (1 << TIF_NOTIFY_RESUME) #define _TIF_SYSCALL_TRACE (1 << TIF_SYSCALL_TRACE) #define _TIF_SYSCALL_AUDIT (1 << TIF_SYSCALL_AUDIT) #define _TIF_POLLING_NRFLAG (1 << TIF_POLLING_NRFLAG) #define _TIF_USING_IWMMXT (1 << TIF_USING_IWMMXT) #define _TIF_RESTORE_SIGMASK (1 << TIF_RESTORE_SIGMASK) #define _TIF_SECCOMP (1 << TIF_SECCOMP) /* Checks for any syscall work in entry-common.S */ #define _TIF_SYSCALL_WORK (_TIF_SYSCALL_TRACE | _TIF_SYSCALL_AUDIT)
例如flags=1,表示_TIF_SIGPENDING即有信号pending;flags=2,表示__TIF_NEED_RESCHED即线程需要调度;
tst r1, #_TIF_WORK_MASK
bne fast_work_pending
_TIF_WORK_MASK的值定于同样位于/arch/arm/include/asm/thread_info.h,值为0xff;这两条指令比较了flags值是否为0,如果不为0,则跳转到fast_work_pending函数。
fast_work_pending根据flags的具体值,跳转到work_resched/do_notify_resume等具体接口继续处理:
fast_work_pending: str r0, [sp, #S_R0+S_OFF]! @ returned r0 work_pending: tst r1, #_TIF_NEED_RESCHED bne work_resched tst r1, #_TIF_SIGPENDING|_TIF_NOTIFY_RESUME beq no_work_pending mov r0, sp @ 'regs' mov r2, why @ 'syscall' tst r1, #_TIF_SIGPENDING @ delivering a signal? movne why, #0 @ prevent further restarts bl do_notify_resume b ret_slow_syscall @ Check work again
// to be continued......