本文介紹了Linux信號發送的代碼執行流程,重點對信號發送結尾的通知機制進行了描述。html
Linux信號處理在網上有不少能夠參考的資料,例以下面連接中的四篇文章就把信號講得很透徹
http://blog.chinaunix.net/uid...
本文重點關注linux信號發送的結尾使用的通知機制,即Linux如何及時通知目的進程「你有信號要處理,趕忙來處理吧」,本文參考的內核代碼爲Linux 4.1.12,硬件架構爲arm64。linux
這一章不是重點,權當作一個備忘筆記。
用戶空間發送信號的命令,kill,其實際代碼執行流程以下
sys_kill->kill_something_info->kill_pid_info->group_send_sig_info->do_send_sig_info->send_signal->__send_signal->(prepare_signal,sigaddset,complete_signal)
具體每一個函數作什麼就不講了,偷個懶。。shell
這個函數是本文關注的重點,由於信號準備好了,最終仍是要通知目標進程儘快處理。咱們知道進程處理信號的時機是在從內核態返回用戶態以前,在ret_to_user函數中經過TIF_SIGPENDING標誌查詢本身有沒有信號要處理。若是有則調用do_notify_resume->do_signal進行處理。架構
sigaddset函數就是實現第一步,即將信號加入進程的pengding->signal位掩碼中。
complete_signal函數就負責實現第二步,通知進程儘快處理,該函數的執行流程以下
complete_signal->signal_wake_up->signal_wake_up_state->wake_up_state,kick_process函數
若是目標進程不在runqueue上,則wake_up_state函數會將其放在runqueue上並返回true;若是進程已經處於runqueue上了,則返回false。ui
void signal_wake_up_state(struct task_struct *t, unsigned int state) { set_tsk_thread_flag(t, TIF_SIGPENDING); /* * TASK_WAKEKILL also means wake it up in the stopped/traced/killable * case. We don't check t->state here because there is a race with it * executing another processor and just now entering stopped state. * By using wake_up_state, we ensure the process will wake up and * handle its death signal. */ if (!wake_up_state(t, state | TASK_INTERRUPTIBLE)) kick_process(t); }
根據signal_wake_up_state函數的代碼邏輯,若是wake_up_state函數返回true,則kick_process不會執行。只有當目標進程已經在runqueue上時,纔會執行kick_process。下面咱們來看看kick_process函數的實現:this
/*** * kick_process - kick a running thread to enter/exit the kernel * @p: the to-be-kicked thread * * Cause a process which is running on another CPU to enter * kernel-mode, without any delay. (to get signals handled.) * * NOTE: this function doesn't have to take the runqueue lock, * because all it wants to ensure is that the remote task enters * the kernel. If the IPI races and the task has been migrated * to another CPU then no harm is done and the purpose has been * achieved as well. */ void kick_process(struct task_struct *p) { int cpu; preempt_disable(); cpu = task_cpu(p); if ((cpu != smp_processor_id()) && task_curr(p)) smp_send_reschedule(cpu); preempt_enable(); }
函數的註釋已經寫得很清楚了,kick_process的目的就是讓進程陷入內核而後在下次返回用戶態以前有機會處理信號。smp_send_reschedule本質就是給進程所在的核發個IPI中斷,從而致使正在運行的進程被打斷陷入內核態。.net
可是發IPI中斷也要知足兩個前提條件:unix
總結一下本文的主要內容,在內核將信號相關信息放到了指定進程的pengding->signal後,須要通知進程儘快處理。若是進程處於內核態,則內核會將該進程喚醒並放入runqueue;若是進程處於用戶態或者說正在運行,則往進程所在的核發送IPI中斷打斷進程,使其有機會在下次返回用戶態時處理信號。code
一個問題:若是進程A和進程B都是實時進程(調度策略是FIFO),A和B都綁定在CPU1上。A的優先級高於B且A正在運行,B也處於運行隊列中。若是此時用戶在shell上經過kill命令但願終止B進程,請問B進程能終止並退出嗎?