1.信號基礎
1.1 信號函數
#include <signal.h>
void (*signal(int signo, void (*func)(int)))(int); 這是可靠寫法
void (* signal( int signo, void (*func)(int) ) )(int);
函數名 :signal
函數參數 :int signo, void (*func)(int)
返回值類型:void (*)(int);
信號的忽略: 信號忽略的核心是將mask中設置0,導致後續與操作始終是0,SIGKILL與SIGSTOP不能被忽略!另外,如果忽略某些由硬件異常產生的信號(例如除以0),則進程的運行行爲是未定義的。
信號的默認動作:大多數的信號處理函數是終止進程!
信號捕捉:爲了做到這一點,要通知內核在某種信號發生時調用一個用戶函數。在用戶函數中,可執行用戶希望對這種事件進行的處理。注意,不能捕捉SIGKILL和SIGSTOP信號。
信號與中斷:信號依賴於中斷!
信號與系統調用:信號是可以打斷系統調用的,所以在執行調用函數的時候,需要增加判斷,errno是否是EINTR,如果是則continue,否則perror(“error”)
1.2 信號產生
產生信號的條件:
- 當用戶按某些終端鍵時,引發終端產生的信號。
- 硬件異常產生信號。
- 進程調用kill(2)函數可將信號發送給另一個進程或進程組。(自然,對此有所限制:接收信號進程和發送信號進程的所有者必須相同,或者發送信號進程的所有者必須是超級用戶。)
- 用戶可用kill(1)命令將信號發送給其他進程。
- 當檢測到某種軟件條件已經發生,並應將其通知有關進程時也產生信號。(這裏指的不是硬件產生的條件,而是軟件條件。)
在信號產生(generation)和遞送(delivery)之間的時間間隔,稱信號是未決的(pending)
1.3可重入函數
信號的函數執行是可以被打斷的,所以會涉及到函數的可重入,系統調用函數都是可重入的,後續在sigaction裏可以設置信號處理函數會自動重啓被打斷的系統調用,一部分庫函數也是可重入的
可衝重入的函數列表如下:
(a)已知它們使用靜態數據結構,(b)它們調用malloc或free,或(c)它們是標準I/O函數。
1.4 實時信號與非實時信號
其中1-31 是非實時信號,是不可靠信號, 34-64是實時信號,是可靠的
非實時信號是通過pending位圖來實現,同時出現多個信號會覆蓋
實時信號是有隊列的,可以ulimit -a來查看pending最大個數,同時出現多個信號會排隊
2. 信號響應策略
信號響應的過程
- 信號從接收到響應是有一個延遲的,因爲需要等待中斷
- 標準信號是沒有嚴格的響應順序的
- 因爲pending位圖的關係,導致如果連續多個標準信號到來,pending會被覆蓋,所以會出現丟失,沒有隊列!
注意點:
- 進程中的信號位圖的初始時刻pending均爲0,mask均爲1,後續可以通過sigaction等來修改
- 信號在用戶態切換到內核態前要進行位與操作,判斷是否有信號
- 只有當有中斷到來後,當前進程進入內核態,信號本身到來並不能立刻響應
- 進程在內核態發現信號後,將mask與pending均置0,當信號處理函數執行完後,會將mask置1,pending置0!