Linux信號基礎
時間:2018-09-28 來源:未知
一、信號使用基礎知識
1.信號的發送與捕捉:

同步:
多個進程協同完成一件事情,當資源不可用時,其他的進程阻塞等待,類比流水線操作,一步一步有先后地完成一個產品。
異步:
多個進程做多項任務,同時運行,互不干擾,當需要建立聯系的時候,停止運行,建立聯系,處理數據,處理完成繼續各自運行。異步多個進程或線程獨自運行,效率較高,系統的實時性用中斷實現。
信號:
內核和用戶空間溝通的信使。直接進行用戶空間進程和內核進程之間的交互,內核進程也可以利用它來通知用戶空間進程發生了哪些系統事件。
eg:
進程結束時,內核會發送一個SIGCHLD信號以通知父進程子進程結束。內核向用戶發送信號時,用戶進程可以進程阻塞。就像項目不能因為一個無名信號出錯而結束進程。
________________________________________
2.信號的周期:

紅色中的的內容都是內核操作,與用戶程序無關,用戶程序就是信號處理。
信號由內核產生,要由用戶空間進行注冊和注銷。信號處理是定義的處理方式,相當于內核到用戶的一次切換。
用戶的處理方式有三種:
1)忽略
不理踩,有兩個信號不可以忽略(KILL和STOP信號);
2)捕捉
捕捉信號后,自定義處理方式,(用signal()進行注冊);
3)默認
系統默認處理方式。
________________________________________
3.信號處理流程:

PS:
實際執行信號的處理動作稱為信號投遞(Delivery),信號從發生到投遞之間的狀態,稱為信號未決(Pending)。
進程可以選擇阻塞(Block)某個信號。被阻塞的信號產生后將保持在未決狀態,直到進程解除對此信號的阻塞,才執行投遞的動作。

每個信號都有兩個標志位分別表示阻塞和未決,還有一個函數指針表示處理動作。
信號產生時,內核在進程控制塊中設置該信號的未決標志(表示某信號是否發生過),直到信號投遞完畢才清除該標志。在上圖的例子中,
1. SIGHUP信號未阻塞也未產生過,當它遞達時執行默認處理動作。
2. SIGINT信號產生過,但正在被阻塞,所以暫時不能投遞。雖然它的處理動作是忽略,但在沒有解除阻塞之前不能忽略這個信號,因為進程仍有機會改變處理動作之后再解除阻塞。
3. SIGQUIT信號未產生過,一旦產生SIGQUIT信號將被阻塞,它的處理動作是用戶自定義函數sighandler。
未決和阻塞標志可以用相同的數據類型sigset_t來存儲,sigset_t稱為信號集,這個類型可以表示每個信號的“有效”或“無效”狀態,在阻塞信號集中“有效”和“無效”的含義是該信號是否被阻塞,而在未決信號集中“有效”和“無效”的含義是該信號是否處于未決狀態。
________________________________________
4.信號應用的場合:

________________________________________
5.常用的幾種信號與默認方式:

SIGALRM定時信號,定時N秒,時間到,發送一個鬧鐘信號,終止進程。
對信號的處理方式解釋:
1)忽略此信號
除了兩種信號(SIGKILL和SIGSTOP)不能被忽略外,其它的的信號都可被忽略,這兩個信號不能被忽略的原因是:它們提供一種終極的終止或停止進程的可靠方法,這是一種終極裁判權,如果這兩個信號都被忽略了的話,某個進程跑飛后就沒有辦法終止或停止這個進程。
另外忽略某些硬件產生的信號被認為是不可取的,如我們如果忽略非法存儲訪問或除以0等硬件產生的信號的話,進程狀態未定義的(無法確定的狀態)。
2)捕獲信號
如果某個進程被通知某個信號發生了,但是想要捕獲這個信號的話,該進程就必須向內核注冊一個捕獲函數,當相應的信號發生時,捕獲函數就會被調用并執行希望對這個事件的處理。
3)執行系統的默認操作
其實我們內核為每個信號在發生時都規定了一個默認的操作,如果我們不捕獲也不忽略的話,當這個信號發生時,進程會按照默認方式去處理這些發生的信號,當然對于絕大多數信號而言,其默認的處理方式都是終止接收到該信號的進程或者忽略此信號。
/**************************************************************************拓展:
查看信號的方法:
kill -l 或man 7 signal
通常有62以上,kill -l 列出的前面32個是非實時的,signal函數只能使用前( 1 -- 31),后面為拓展信號,signal不能用,中間缺少的沒有編號。前面(1 -- 31)是非實時的,信號不支持排隊,可能造成信號丟失。有預定義的含義和處理方式函數。后面的實時信號支持排隊
各種信號說明:
//www.2cto.com/os/201608/537204.html (感謝博主詳細列出了各種信號)
***************************************************************************/
我們常用的有:
INT, QUIT, KILL, USR1, USR2 ALRM STOP TERM .
USR1,和USR2在硬件中沒有任何價值,在應用程序中,可以使用usr1和usr2進行自定義通信。
TTOU/TTIN網絡開發中常用的信號,IO信號在網路常用。
________________________________________
二、相關系統調用:
1.kill()和raise()

pid = -1,向所有進程發送信號(除了init進程)。
sig = 0;表示只檢查錯誤信息,不發送信號。沒有0號編號信號。

kill示例:

raise示例:

當前進程進入stop態,發送CONT信號繼續運行。如果發送的是USR1信號,默認進程退出。
________________________________________
2.alarm()和pause()

0.alarm是指定一定時間后發送信號。
1.要注意的是,一個進程只能有一個鬧鐘時間。如果在調用alarm時已設置過鬧鐘時間,則之前的鬧鐘時間被新值所代替。
2.alarm函數只設置時鐘,設置完成后,程序向下繼續運行。(補充說明alarm函數只是設置定時鬧鐘,并不阻塞);
3.等待信號來臨可以利用while檢測等待或利用pause函數將進程掛起。
pause()接收任意信號就會結束掛起。

示例代碼: myalarm.c

mypause.c

利用alarm和pause實現sleep功能:

補充:
alarm()和sleep()函數不同,alarm信號并不會阻塞,相當于訂了鬧鐘后,系統在后臺計時,到了指定時間就會發送ALRM信號;sleep()函數會使進程進入睡眠態,進程暫停執行,直到時間結束。

