信號(signal)

   所謂信號,就簡單場景來說,啓動一個前臺進程,用戶按下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中。


幾個函數:

  1. kill函數:給指定的進程發送指定的信號

    函數原型:int kill(pid_t pid,int signo)

    返回值:成功爲0,失敗爲-1

  2. rasie函數:給當前進程發送指定的信號

    函數原型:int raise(int signo)

    返回值:成功爲0,失敗爲-1

  3. abort函數:當前進程接受到SIGABRT信號而異常終止

    函數原型:void abort(void)

  4. alarm函數:在seconds秒後給當前進程發SIGALRM信號,默認終止當前進程

    unsigned int alarm(unsigned int seconds)

    返回值:返回0或者設定鬧鐘剩下的時間

    當seconds爲0時,表示取消該鬧鐘,並返回設定鬧鐘剩下的時間。


  5. signal函數:設置某一信號的對應動作

    函數原型:

    wKiom1eXY_SjncZ2AAAbEEeCJM0101.png  


檢驗當seconds爲0時,取消鬧鐘,函數返回以前設定的鬧鐘剩下的時間

wKioL1eXY_Sh6lawAABWuH1fqNM016.png



wKioL1eXZbOiqp2dAADX1ftg28Y796.png


二、阻塞信號

基本概念:

  1. 實際執行信號的處理動作稱爲信號抵達忽略是遞達的一種

  2. 信號從產生到遞達之間的狀態稱爲信號未決

  3. 進程可以選擇阻塞某一信號。被阻塞的信號將保持在未決狀態,直到進程解除對此信號的阻塞,才能執行遞達的動作。



 信號在內核中的表示示意圖:

wKioL1eXZ8KxTZDnAABqddkoiW0517.png

每個信號都有兩個標誌位分別爲block(阻塞)和pending(未決),還有一個函數指針表示處理動作。

  1. SIGUP:信號未產生也爲阻塞,當它遞達時執行默認處理動作

  2. SIGINT:信號產生,但是阻塞。它的處理動作是忽略。阻塞不解除,都不會執行處理動作。

  3. SIGQUIT:沒有信號產生,一旦產生就會被阻塞,處理動作是用戶自定義函數sighandler。



每個信號只有一個bit的未決標誌和阻塞標誌,非0即1,不記錄該信號產生的次數。因此,未決和阻塞標誌可以用相同的數據類型sigset_t儲存,sigset_t稱爲信號集。這個類型表示信號的有效和無效。在阻塞信號集(信號屏蔽字)中即block表,1表示信號阻塞,0表示不阻塞。在未決信號集即pending表,1表示信號產生未決狀態,0表示沒有產生信號。總而言之,信號集爲能夠表示信號類型的0,1序列


注:不可使用位操作操作信號集,有專有的函數操作


信號集操作函數:

wKioL1eXccyR5JwEAABIzEmRaak696.png

sigemptyset:初始化set所指向的信號集,使其中所有的信號對應的bit清零

sigfillset:初始化set所指向的信號集,使其中所有的信號對應的bit置1

sigaddset:在該信號集中添加某一信號

sigdelset:在該信號集中刪除某一信號

sigismember:判斷一個信號集的有效信號中是否包含某種信號,若包含返回1,不包含返回0,出錯返               回-1


sigprocmask函數:讀取或更改進程的信號屏蔽字(阻塞信號集)

wKioL1eXc2vxzsqgAAAdBn_xyPg925.png

返回值:成功爲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次權限轉換>:

 wKioL1eXjKyhteJmAAG7abIug3A491.png

   

前面曾提過當一個進程收到信號時,並不是立即處理,而是在合適的時候,所謂的合適的時候就是

從內核態返回用戶態時切換處理信號當內核處理完異常或中斷時,會先檢查當前進程中是否有可以被遞達的信號,有,如果信號的處理方式捕捉:從內核態調到用戶態執行代碼,之後返回內核態,從內核態返回用戶態即上次被中斷或者異常的地方。若信號的處理方式爲默認,則終止進程,若爲忽略,從Pending表中刪除該信號,即將1變爲0,直接跳到用戶態。


sigaction函數:讀取和修改與指定信號相關聯的處理動作。

wKiom1eXjj6w-sciAAAlU9rxVAQ528.png

返回值:成功爲0,出錯爲-1.

signo:指定信號的編號

act:非空。根據act修改該信號的處理動作

oact:非空。傳出該信號原來的處理動作

act和oact指向如下結構體:

wKiom1eXjj-CWNDNAACus2Ag92A820.png

sa_hander:若賦值爲SIG_IGN 表示忽略信號;SIG_DFL表示執行默認動作;賦值爲一個函數指針表示自定義函數捕捉信號。這個函數不是被main函數調用,而是被系統函數所調用。

sa_mask:需要額外屏蔽的信號,當信號處理函數返回時自動恢復原來的信號屏蔽字

sa_flags:包含一些選項


pause函數:使調用進程掛起直到有信號遞達

函數原型:int pause(void)      返回值:只有出錯的返回值

  1. 信號的處理動作爲終止進程,則進程終止,不執行pause

  2. 信號的處理動作爲忽略,進程繼續掛起,pause不返回

  3. 信號的處理動作爲捕捉,則調用了信號處理函數後返回-1


實現sleep函數

wKioL1eXk-2CGGfYAABK35w5z_g169.png

測試結果:每個1秒打印一個hello bit

wKiom1eXk-3ioGIrAAAVtfol8AY221.png



測試31個普通信號中那些能被捕捉,那些不能被捕捉。


wKioL1eXif2R4TsvAABHUnZV2gI338.png

   

測試結果:

wKiom1eXif3hjI2QAAAwvYl_FKE915.pngwKiom1eXif6DZgNOAAA1SsUDd8s451.png


分析:首先對每個信號都設置對應的行爲,當信號爲9時,直接終止進程,若將信號9除外,當信號爲19 時,進程終止。若將信號9,19除外,則每個信號都可以被捕捉。

   



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章