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()
函數執行信號處理程序。