From: http://blog.csdn.net/guosha/archive/2008/09/17/2943615.aspx
Linux為實現訊號處理提供了比較多的介面,看似紛雜,但理清訊號的關係後還是很有條理的。主要分為以下四組,怎麼發送一個訊號,收到一個訊號後做什麼樣的處理,主動等待一個訊號的發生,對特定訊號只記錄是否發生供以後再處理;四組的API如下(不考慮多線程):
發送訊號 kill, alarm, arise;
安裝訊號 signal, sigaction;
等待訊號 sigsuspend, sigwait,pause;
阻塞訊號 sigfillset, sigemptyset, sigaddset, sigdelset, sigismember, sigprocmask;
發送訊號, kill, alarm, arise
這是唯一一組由訊號發送方調用的API,其中
alarm,
arise都是給自己進程產生訊號,而kill是可以從一個進程發送一個訊號到另一個進程,因為訊號的接收方一般都不做許可權檢查,所以發送方要有許可權給的
接收方發送訊號訊號才能被發送,要不然我就可以寫個非法程式,直接使用kill給進程1發送一個SIGKILL訊號的話,你的系統基本也就OVER了。這
個許可權檢查的指導原則按我的理解是,發送方的有效使用者是否有許可權給接收方的實際使用者發送訊號來決定。因為,當你運行一個seteuid程式,這個程式運行
時的euid可能不再是ruid了,但是你仍然可以kill它。arise()的實現我猜就是用kill(getpid(),
signo)來實現。另外系統核心檢測到一些異常或是狀態改變時也會給進程發送訊號,如SIGINTR,SIGCHILD等等。
安裝訊號, signal, sigaction
這組API是決定收到一個訊號號的處理方法。如果不顯
示的安裝一個訊號的處理方法,系統都有對應的預設處理方法,大多數都是直接終止程式本身. 在richard
stevens的APUE中說signal的訊號處理需要重新安裝訊號處理方法,但我在FC5下測試,不重新安裝也是可以的,並且進行中訊號處理時,發
送一個訊號過去,該訊號也不會丟失,只是暫時阻塞,等前面的訊號處理完後再處理。當然當正在訊號處理時發送多個訊號過去,訊號處理完後,可能傳遞一個訊號
過去,其它的訊號會丟失。至於直接使用signal安裝訊號,等訊號處理完後是否會重啟被該訊號中斷的慢速系統調用,我沒有測試,不得而知。
當然你可以使用sigaction來安裝訊號,並顯示的指明restart標誌,這樣被該訊號中斷的系統調用就會自動重啟。sigaction這個API
更複雜,功能也更多靈活,可以隨意定製,通過siginfo_t的結構,可以得到很多訊號相關的資訊。我猜現在的linux的signal都是用
sigaction封裝來實現的。跟sigaction相比,signal簡單多了,沒理由不用它,當然,你得保證signal的語義不會對你的程式造成
衝突。
等待訊號 pause, sigsuspend, sigwait
這組API都是等待訊號的發生,可以
根據程式的實際需求來選取。pause只要接收到訊號就會返回,sigsuspend跟sigwait都是只等待某些特定的訊號發生,但
sigsuspend是把參數裡的訊號集給阻塞,而sigwait一般是先調用sigprocmask把所有的訊號都阻塞,然後再等待sigwait參數
裡指定的訊號。通常sigwait用在多線程程式裡,主線程把所有訊號都mask掉,然後不同的線程用sigwait來等待自己感興趣的訊號。
阻塞訊號 sigfillset, sigemptyset, sigaddset, sigdelset, sigismember, sigprocmask
這一組API最多也最簡單,前面五個API都是對訊號集的操作,用它用得到一個你想要的訊號集後就可以調用sigprocmaks的設定程式的訊號掩碼了。
另外, siglongjmp, sigsetjmp是安全的在訊號處理常式裡跑轉的兩個介面。
最後一些跟訊號處理常式裡的一些注意事項
1、因為訊號會中斷慢速的系統調用,所以當系統調用失敗時,一定要檢查失敗原因,當被訊號中斷時做出重啟系統調用的動作;
2、因為訊號處理有非同步性,所以任何在訊號處理常式裡調用了不具可重新進入性的函數都要小心了。這裡的不具可重新進入性的函數包括了系統調用跟自己定義的函數。
3、在訊號處理常式裡任何可能導致對全域變理的修改也要小心了,一個經常被人忽視的就是在訊號處理常式裡調用了一個系統調用後可能導致errno的修改。