Linux内核中由于不同原因(例如非法地址、按下Ctrl+C、用户进程系统调用等等)向进程发送信号的函数调用路径最后公用的函数是send_signal()
,这个函数位于linux-3.13/kernel/signal.c文件中。这个函数接下来的调用链为complete_signal()->signal_wake_up()->signal_wake_up_state()->wake_up_state()->try_to_wake_up()
函数,从上一篇文章可以知道try_to_wake_up()
函数做了三件事:将任务重新添加到就绪队列,将运行标志设置为TASK_RUNNING
,如果被唤醒的任务可以抢占当前运行任务则设置当前任务的TIF_NEED_RESCHED
标志。
在发生中断时,系统在将中断向量号压入堆栈后,会跳转到linux-3.13/arch/x86/kernel/entry_32.S
中的common_interrupt
处运行,其代码如下:
common_interrupt:
ASM_CLAC
addl $-0x80,(%esp) /* Adjust vector into the [-256,-1] range */
SAVE_ALL
TRACE_IRQS_OFF
movl %esp,%eax
call do_IRQ //调用do_IRQ函数,是个C函数,在此函数中处理中断
jmp ret_from_intr //从中断返回
ENDPROC(common_interrupt)
其中do_IRQ()
是个C函数,它先取得对应的中断请求描述符,然后执行该中断请求描述符中的中断处理函数,最后软中断也会在这个函数中运行。执行完do_IRQ()
函数之后,会跳转到ret_from_intr
处,之后的执行序列会先检查TIF_NEED_RESCHED
标志,决定是否进行调度,然后会进行信号的投递,代码如下:
work_pending:
testb $_TIF_NEED_RESCHED, %cl
jz work_notifysig
work_resched:
call schedule
LOCKDEP_SYS_EXIT
DISABLE_INTERRUPTS(CLBR_ANY) # make sure we don't miss an interrupt
# setting need_resched or sigpending
# between sampling and the iret
TRACE_IRQS_OFF
movl TI_flags(%ebp), %ecx
andl $_TIF_WORK_MASK, %ecx # is there any work to be done other
# than syscall tracing?
jz restore_all
testb $_TIF_NEED_RESCHED, %cl
jnz work_resched
work_notifysig: # deal with pending signals and
# notify-resume requests
#ifdef CONFIG_VM86
testl $X86_EFLAGS_VM, PT_EFLAGS(%esp)
movl %esp, %eax
jne work_notifysig_v86 # returning to kernel-space or
# vm86-space
1:
#else
movl %esp, %eax
#endif
TRACE_IRQS_ON
ENABLE_INTERRUPTS(CLBR_NONE)
movb PT_CS(%esp), %bl
andb $SEGMENT_RPL_MASK, %bl
cmpb $USER_RPL, %bl
jb resume_kernel
xorl %edx, %edx
call do_notify_resume //在这个函数中进行信号处理函数的执行
jmp resume_userspace
然后在do_notify_resume()
中会调用do_signal()
函数执行信号处理程序。