歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux編程 >> Linux編程 >> Linux編程---信號處理

Linux編程---信號處理

日期:2017/3/1 9:42:46   编辑:Linux編程

信號是一種異步的進程間通信的方式.但是這種通知方式能交換的信息很少.只能說給一個事件的標志.類似單片機中的中斷,強迫進程停止做當前應當做的事情,而去執行中斷執行程序。

信號的產生有如下幾種:

1.用戶按下了某個終止鍵,如ctrl-\或ctrl-c.是由終端程序向當前進程發送一個中斷信號.

2.程序異常.比如除零錯誤.

3.kill函數向其發送了一個終止信號

4.進程向自己發送信號.如進程調用alarm函數.

5.企圖讀寫終端的後台進程會得到作業的控制信號SIGTTIN或SIGTOU.

6.當進程超越了CPU或文件的大小限制時,內核會生成一個信號.

Linux的信號處理和實際使用(結合Redis分析) http://www.linuxidc.com/Linux/2014-03/98787.htm

Linux-信號處理 http://www.linuxidc.com/Linux/2012-06/63883.htm

一步一步學Linux C:信號處理潛在危險!!! http://www.linuxidc.com/Linux/2012-03/57195.htm

一步一步學Linux C:信號處理方法 && 實際應用 http://www.linuxidc.com/Linux/2012-03/57074.htm

Linux信號簡介和信號處理相關函數 http://www.linuxidc.com/Linux/2011-04/34500.htm

得到信號的進程也有三種方式應對.

1.忽略信號.大部分信號都可以忽略,但SIGSTOP和SIGKILL除外

2.捕獲信號.這就要進程對信號進行專門的設置了.

3.執行系統默認動作.這個主要是系統默認對信號規定的默認動作.

---流產: 終止進程並且生成內存轉儲文件.即產生存儲上下文環境,寄存器內容等信息的core文件.(abort函數)

---終止: 這個不產生core文件

---忽略: 忽略信號

---掛起: 暫停進程

---繼續: 恢復進程.



對於進程正處在可中斷的睡眠狀態並且這個信號是非阻塞的,內核便喚醒此進程使它能夠接受信號.

對於信號,進程可以立即處理,也可以選擇讓其懸掛.對於不可靠信號,一個進程對同一種信號只能懸掛一個.也就是信號可以存儲,但是只能存一個.對於可靠信號,信號來幾個就執行幾次信號句柄,信號可以排隊,所以可以用來計數.

對於小於SIGRTMIN的信號為不可靠信號,SIGRTMIN-SIGRTMAX為可靠信號.實際上實時對應可靠,非實時對應不可靠.



這裡是我抄網上的,測試版本為Ubuntu 12.04LTS.我之前也不太明白實時和非實時,可靠和不可靠信號的差別.如果理解有誤還請指出.



這裡介紹一個NSIG宏.這個宏記錄了系統中允許的信號總數+1.



void psignal(int signo,const char *msg);

這個函數我第一次見...網上貌似也沒資料.....

若msg是控制正,函數只打印signo對應的描述,並隨後帶上一個換行符.

若msg是非空指針,平函數用這個字符串作為其輸出消息的前綴,並且在此前綴和signo對應的消息之間放置一個冒號和空格將他們分開.



簡單來說就是查詢信號用途的函數...



不同信號類型中的信號也蠻多的...具體的還是以後編程遇到後慢慢記吧.這裡就不寫了.



還是寫一些函數的說明吧...



一.信號生成

int raise(int sig);

這個函數功能就是發送sig到調用進程...

如果sig信號設置了句柄,那麼raise只有在句柄函數返回時才會返回.



int kill(pid_t pid,int sig);

這個函數用的比較多吧.雖說叫kill,但實際上是發送信號的函數.並不一定只能發送kill.

如果sig為0,那麼kill只進行正常的錯誤檢查而不實際發送信號,這常常用來檢查pid的合法性.如果發送信號的進程可能不在的話,那麼就可以這麼用.

pid實際上參數用法還蠻多的.

>0 發送信號給pid進程

=0 發送給進程所在的進程組的所有進程.

=-1 廣播信號,即發送給能發送的所有進程.

<-1 發送信號給進程組ID等於pid絕對值.並且發送者有權向它發送信號的所有進程.



這個pid的取值和之前的wait很像.



這裡還提到了權限的問題.一般進程的實際用戶ID或有效用戶ID必須與接受信號進程的實際用戶ID或有效用戶ID相同.對於有效ID是超級用戶的進程來說,可以向任何進程發送信號.





二.設置信號動作.

typedef void (*sighandler_t)(int );

sighandler_t signal(int signum,sighandler_t handler);

兩個參數:signum設置型號類型.handler設置信號句柄.

handler其實可以取以下三種值之一:

SIG_DFL 默認動作

SIG_IGN 忽略該信號.但SIGKILL和SIGSTOP這兩個信號,不能設置為忽略.盡量不要忽略系統相關的信號.

函數指針 就是執行這個函數體的函數.參數一般得到的是信號類型.



這個函數的返回值是前一次有效動作的指針.,因此它的原型與第二個參數相同.當signal調用成功時.返回值要麼是SIG_DFL,SIGIGN或函數句柄.



如果調用出錯,那麼則會返回SIG_ERR並設置errno.唯一的錯誤嗎是EINVAL



對於fork和exec來說,信號相關對於fork來說是繼承的.

對exec來說則不繼承.所有忽略的信號繼續忽略,其他有句柄的則改為默認動作.





由於signal函數使用的時候存在一個時間窗口,信號很可能不可靠.所以有了下面這個信號函數.通常用它的比較多

int sigaction (int signum,const struct sigaction *act,struct sigaction *oact);

第二個參數是一個結構體,其中包含了信號句柄.第三個參數則是原來的信息.

struct sigaciton{

void (*sa_handler)(int);

void (*sa_sigaction)(int ,siginfo_t *,void *);

sigset_t sa_mask;

int sa_flags;

}

sa_handler就是句柄.

sa_sigaction也是個句柄地址,當sa_flags設置了SA_SIGINFO的時候才起作用,否則用handler.

sa_mask指明信號句柄執行期間要阻塞的一組信號,在sigaction執行過程中會屏蔽這些信號.

sa_flags可取的值就多了,有以下這些:
SA_NOCLDSTOP

此標志只對SIGCHLD信號有效,正常情況下,子進程被停止時,總是向父進程發送信號,有時候被停止的子進程恢復運行時,可能向父進程發送信號.當signum參數傳值是SIGCHLD時,在這兩種情況下都不再發送信號.

SA_RESTART

如果設置該標志並且捕獲信號,系統將在信號句柄返回時自動恢復被該信號中斷了的系統調用.否則被中斷的系統調用將返回-1並置errno為EINTR.多數情況下sa_flags的值為SA_RESTART.

SA_ONSTACK

如果設置此標志,系統將在用sigaltstack指定的替代信號棧上運行的信號句柄.否則使用用戶棧來交付信號.

SA_NODEFER

如果設置此標志並且捕獲信號,在信號句柄執行期間系統不自動阻塞該信號.這對應於不可靠信號signal的情形.(估計沒什麼人會用這個吧...)

SA_RESETHAND

如果設置此標志並且捕獲信號,在信號句柄的入口,系統將重置信號動作為SIG_DFL並清楚SA_SIGINFO標志,並且sigaction的行為就和signal差不多了.

SA_NOCLDWAIT

只對SIGCHLD起作用.如果設置此標志,並且sig是SIGCHLD,調用進程的所有子進程在終止時不轉變成僵死進程.這樣父進程就不必調用wait函數了.如果調用了wait則進程會一直阻塞直到所有子進程都結束才返回,返回值為-1和設置errno為ECHILD.

SA_SIGINFO

這個就是上面的情況了.未設置用sa_handler並且禁止修改sa_sigaction.

句柄原型必須是void f(int signo);

如果設置了這個標志,則必須修改sa_sigaction設置信號句柄並禁止修改sa_handler.

句柄原型為void f(int signo,siginfo *info,void *context);

這個有些復雜,不過還是可以說清楚的.

實際上在siginfo_t中包含了更多的信號產生的信息.context也指向了一個進程上下文環境.Linux下默認類型為sigcontext結構體.

這個內容好多...我就不展開了,我看APUE上有相關信息,用到再查吧.

更多詳情見請繼續閱讀下一頁的精彩內容: http://www.linuxidc.com/Linux/2014-06/103489p2.htm

Copyright © Linux教程網 All Rights Reserved