1,在此之前,必須先了解幾個概念:
訊號遞達(delivery):實際執行訊號處理的動作。
訊號未決(pending):訊號從產生到遞達之間的狀態。
訊號阻塞(block):被阻塞的訊號產生時將保持在未決狀態,直到 程序解除對此訊號的阻塞,才 執行遞達的動作。
注意:訊號阻塞和訊號忽略是不同的。只要訊號被阻塞就不會遞達,除非解除阻塞,而忽略是在遞達之後 可選的一種處理動作。
乙個訊號處於pending狀態,遮蔽之後,它永遠不會被delivery 。
乙個訊號是否立即delivery ,與block沒有絕對關係。
乙個訊號是否被block與是否pending沒關係。
2,訊號在核心中的表示—-每個程序都會維護這3張表:
每個訊號都有兩個標誌位分別表示阻塞(block)和未決(pending),還有乙個函式指標表示處理動作。訊號產生時,核心在程序控制塊中設定該訊號的未決標誌,直到訊號遞達才清除該標誌。
3張表的儲存:
pending表:用4個位元組的點陣圖表示,點陣圖的位置表示訊號編號,內容表示是否pending。
block表:用4個位元組的點陣圖表示,點陣圖的位置表示訊號編號,內容表示是否block。
handler表:是乙個控制代碼函式指標,陣列即可表示,下標表示訊號編號,內容表示訊號處理的動作,為null表示沒有處理該訊號。
分析上圖中的訊號:
1〉sighup訊號未阻塞也未產生過,當它遞達時執行預設處理動作。
2〉sigint訊號產生過,但正在被阻塞,所以暫時不能遞達。雖然它的處理動作是忽略,但在沒有解除阻塞之前不能忽略這個訊號,因為程序仍有機會改變處理動作之後再解除阻塞。
3〉 sigquit訊號未產生過,一旦產生sigquit訊號將被阻塞,它的處理動作是使用者自定義函式sighandler。
3,如果在程序解除對某訊號的阻塞之前這種訊號產生過多次,將如何處理?
linux是這樣實現的:常規訊號在遞達之前產生多次只計一次,而實時訊號在遞達之前產生多次可以依次放在乙個佇列⾥裡。從上圖來看,每個訊號只有一 個bit的未決標誌,非0即1,不記錄該訊號產生了多少次,阻塞標誌也是這樣表示的。
因此,未決和阻塞標誌可以用相同的資料型別sigset_t來儲存,sigset_t稱為訊號集,這個型別可以表示每個訊號的「有效」或「無效」狀態,在阻塞訊號集中「有效」和「無效」的含義是該訊號是否被阻塞,而在未決訊號集中「有效」和「無效」的含義是該訊號是否處於未決狀態。阻塞訊號集也叫做當前程序的訊號遮蔽字(signal mask),這裡的 「遮蔽」應該理解為阻塞而不是忽略。
注意:訊號集和訊號量級二者之間沒有任何關係。
當利用訊號量機制解決了單個資源的互斥訪問後,我們討論如何控制同時需要多個資源的互斥訪問,訊號量集是指同時需要多個資源時的訊號量操作。
4,核心是如何處理訊號的?
從圖中我們能發現,當乙個正在執行的程序收到了中斷或者是呼叫了系統呼叫,則該程序會從使用者態進入到核心態,當程序準備從核心態返回到使用者態時,核心會檢查要返回的程序的pcb中的signal點陣圖資訊,如果當前的pengding表中有標誌1,那麼核心會把pengding鍊錶中懸掛的訊號拿出來程序處理,處理的過程如下:
如果handler指向了使用者自定義的處理函式,那麼會先從核心態返回到使用者態執行完處理函式後再返回到核心態,最後再從核心態返回到使用者態中因為中斷或者系統呼叫進入核心態的**從而繼續執行。
5,訊號集操作函式
訊號集:sigsize_t型別,增刪改查不能呼叫系統的自增自減或加減操作,必須通過呼叫系統函式來實現。
#include
int sigemptyset(sigset_t *set);//初始化set所指向的訊號集,使其中所有訊號的對應bit位清零,表示該訊號集不包含任何有效訊號。
int sigfillset(sigset_t *set);//初始化set所指向的訊號集,使其中所有訊號的對應bit置位1,表示該訊號集的有效訊號包括系統支援的所有訊號
int sigaddset(sigset_t *set, int signo);//為訊號集中新增
某種有效訊號
int sigdelset(sigset_t *set, int signo);//為訊號集中刪除某種有效訊號
int sigismember(const sigset_t *set, int signo);//判斷⼀乙個訊號集的有效訊號中是否包含某種訊號,若包含則返回1,不包含則返回0,出錯返回-1
這五個函式都是成功返回0,出錯返回-1。
6,函式sigprocmask可以讀取或更改程序的訊號遮蔽字(阻塞訊號集)
#include
int sigprocmask(int how, const sigset_t *set, sigset_t *oset);
返回值:若成功則為0,若出錯則為-1。
引數:
oset:如果oset是非空指標,則讀取程序的當前訊號遮蔽字通過oset引數傳出。如果set是非空指標,則更改程序的訊號遮蔽字,引數how指示如何更改。
set:如果oset和set都是非空指標,則先將原來的訊號遮蔽字備份到oset裡,然後根據set和how引數更改訊號遮蔽字。
how引數的可選值為:
如果呼叫sigprocmask解除了對當前若干個未決訊號的阻塞,則在sigprocmask返回前,至少將其中乙個訊號遞達。
7,sigpending讀取當前程序的未決訊號集,通過set引數傳出。
#include
int sigpending(sigset_t *set);
返回值:呼叫成功則返回0,出錯則返回-1。
8,以上函式的測試用例
makefile:
1 sigset:sigset.c
2 gcc -o sigset sigset.c
3.phony:clean
4 clean:
5 rm -f sigset
sigset.c:
1
/**************************************
2 *檔案說明:sigset.c
5 *開發環境:kali linux/g++ v6.3.0
6 ****************************************/
7#include
8#include
9#include
10#include
1112
void showpending(sigset_t* pending)//列印當前程序的pending表
13 22
printf("\n");
23 }
2425
void handler(int sig)
26 3031
int main()
32 54 }
55return
0; 56 }
程式執行時,每秒鐘把各訊號的未決狀態列印一遍,由於我們阻塞了sigint訊號,按ctrl-c將會使sigint訊號處於未決狀態,按ctrl-\仍然可以終止程式,因為sigquit訊號沒有阻塞。
執行結果:
Linux入門 訊號(二) 阻塞訊號
實際執行訊號的處理動作稱為訊號遞達 delivery 訊號從產生到遞達之間的狀態,稱為訊號未決 pending 程序可以選擇阻塞 block 某個訊號。被阻塞的訊號產生時將保持在未決狀態,直到程序接觸對此訊號的阻塞,才執行遞達的操作。阻塞和忽略不同,只有訊號阻塞就不會遞達,而忽略是在訊號遞達之後可選...
linux訊號 阻塞訊號
1.訊號在核心中的表示 我們知道了訊號產生的各種原因,而實際執行訊號處理的動作,叫做訊號遞達 delivery 訊號從產生到遞達之間的狀態,稱為訊號未決 pending 程序可以選擇阻塞 block 某個訊號。被阻塞的訊號產生時將保持在未決狀態,直到程序解除對此訊號的阻塞,才執行遞達的動作。注意,阻...
Linux 阻塞訊號
linux中訊號產生的原因大致有一下三種 鍵盤中斷 命令發出 異常產生中斷 但歸根結底,這些訊號其實都是最終有作業系統發出的。常見的對訊號的處理,無外乎以下三種 忽略終止該程序 自定義行為 對訊號的處理動作叫做訊號遞達,在訊號由產生到遞達的過程中還有一種狀態叫做未決。即訊號雖產生,但是未被處理。這個...