所謂信號,就簡單場景來說,啓動一個前臺進程,用戶按下Ctrl_C可將進程終止。
在這呢,簡單說說後臺進程能否用Ctrl_C終止?
一個命令後面加個&便可在後臺執行。這樣Shell不必等待進程結束就可以新的命令,啓動新的進程。Shell可以同時執行一個前臺進程和多個後臺進程,只有前臺進程才能接到Ctrl_C控制鍵產生的信號。
若在前臺執行死循環,可用Ctrl_C,kill -9 id 將進程終止
若在後臺執行死循環,Ctrl_C不會將進程殺死,kill -9 id可以將進程終止,kill -11 id也可以將進程終止,但是產生段錯誤。
一、產生信號
基本概念:
1. 如何產生信號呢?
<1> 鍵盤。當某個進程正在運行時,按下Ctrl_C便可終止進程。
<2> 命令。如kill -9 3212 將id爲3212的進程終止。
<3> 函數。如kill,alarm,abort函數。
<4> 操作系統捕捉。軟件,硬件異常
2. 如何處理信號?
<1> 忽略信號
<2> 執行該信號的默認動作,一般是將該進程終止
<3> 捕捉。自定義函數。
3. 當進程在收到信號時,並不是立即處理,而是在合適的時候處理。應先將所接受的信號先保存在自己 的PCB中。
幾個函數:
kill函數:給指定的進程發送指定的信號
函數原型:int kill(pid_t pid,int signo)
返回值:成功爲0,失敗爲-1
rasie函數:給當前進程發送指定的信號
函數原型:int raise(int signo)
返回值:成功爲0,失敗爲-1
abort函數:當前進程接受到SIGABRT信號而異常終止
函數原型:void abort(void)
alarm函數:在seconds秒後給當前進程發SIGALRM信號,默認終止當前進程
unsigned int alarm(unsigned int seconds)
返回值:返回0或者設定鬧鐘剩下的時間
當seconds爲0時,表示取消該鬧鐘,並返回設定鬧鐘剩下的時間。
signal函數:設置某一信號的對應動作
函數原型:
檢驗當seconds爲0時,取消鬧鐘,函數返回以前設定的鬧鐘剩下的時間
二、阻塞信號
基本概念:
實際執行信號的處理動作稱爲信號抵達。忽略是遞達的一種。
信號從產生到遞達之間的狀態稱爲信號未決
進程可以選擇阻塞某一信號。被阻塞的信號將保持在未決狀態,直到進程解除對此信號的阻塞,才能執行遞達的動作。
信號在內核中的表示示意圖:
每個信號都有兩個標誌位分別爲block(阻塞)和pending(未決),還有一個函數指針表示處理動作。
SIGUP:信號未產生也爲阻塞,當它遞達時執行默認處理動作
SIGINT:信號產生,但是阻塞。它的處理動作是忽略。阻塞不解除,都不會執行處理動作。
SIGQUIT:沒有信號產生,一旦產生就會被阻塞,處理動作是用戶自定義函數sighandler。
每個信號只有一個bit的未決標誌和阻塞標誌,非0即1,不記錄該信號產生的次數。因此,未決和阻塞標誌可以用相同的數據類型sigset_t儲存,sigset_t稱爲信號集。這個類型表示信號的有效和無效。在阻塞信號集(信號屏蔽字)中即block表,1表示信號阻塞,0表示不阻塞。在未決信號集即pending表,1表示信號產生未決狀態,0表示沒有產生信號。總而言之,信號集爲能夠表示信號類型的0,1序列。
注:不可使用位操作操作信號集,有專有的函數操作
信號集操作函數:
sigemptyset:初始化set所指向的信號集,使其中所有的信號對應的bit清零
sigfillset:初始化set所指向的信號集,使其中所有的信號對應的bit置1
sigaddset:在該信號集中添加某一信號
sigdelset:在該信號集中刪除某一信號
sigismember:判斷一個信號集的有效信號中是否包含某種信號,若包含返回1,不包含返回0,出錯返 回-1
sigprocmask函數:讀取或更改進程的信號屏蔽字(阻塞信號集)
返回值:成功爲0,失敗爲-1.
set:非空,更改進程的信號屏蔽字,參數how指示如何更改
set,oset:非空,則先將原來的信號屏蔽字備份到oset,然後根據set,how更改信號屏蔽字
how參數的取值:(假設信號屏蔽字爲mask)
SIG_BLOCK:set包含了添加到當前信號屏蔽字的信號,相當於mask = mask | set
SIG_UNBLOCK:set包含從當前信號屏蔽字中解除阻塞的信號,相當於mask = mask&~set
SIG_SETMASK:設置當前信號屏蔽字爲set所指向的值,相當於mask = set
sigpending函數:讀取當前進程的未決信號集
int sigpending(sigset_t *set);
返回值:成功爲0,出錯爲-1
三.捕捉信號
信號捕捉過程<4次權限轉換>:
前面曾提過當一個進程收到信號時,並不是立即處理,而是在合適的時候,所謂的合適的時候就是
從內核態返回用戶態時切換處理信號。當內核處理完異常或中斷時,會先檢查當前進程中是否有可以被遞達的信號,有,如果信號的處理方式捕捉:從內核態調到用戶態執行代碼,之後返回內核態,從內核態返回用戶態即上次被中斷或者異常的地方。若信號的處理方式爲默認,則終止進程,若爲忽略,從Pending表中刪除該信號,即將1變爲0,直接跳到用戶態。
sigaction函數:讀取和修改與指定信號相關聯的處理動作。
返回值:成功爲0,出錯爲-1.
signo:指定信號的編號
act:非空。根據act修改該信號的處理動作
oact:非空。傳出該信號原來的處理動作
act和oact指向如下結構體:
sa_hander:若賦值爲SIG_IGN 表示忽略信號;SIG_DFL表示執行默認動作;賦值爲一個函數指針表示自定義函數捕捉信號。這個函數不是被main函數調用,而是被系統函數所調用。
sa_mask:需要額外屏蔽的信號,當信號處理函數返回時自動恢復原來的信號屏蔽字
sa_flags:包含一些選項
pause函數:使調用進程掛起直到有信號遞達
函數原型:int pause(void) 返回值:只有出錯的返回值
信號的處理動作爲終止進程,則進程終止,不執行pause
信號的處理動作爲忽略,進程繼續掛起,pause不返回
信號的處理動作爲捕捉,則調用了信號處理函數後返回-1
實現sleep函數
測試結果:每個1秒打印一個hello bit
測試31個普通信號中那些能被捕捉,那些不能被捕捉。
測試結果:
分析:首先對每個信號都設置對應的行爲,當信號爲9時,直接終止進程,若將信號9除外,當信號爲19 時,進程終止。若將信號9,19除外,則每個信號都可以被捕捉。