1. 程式人生 > 其它 >12訊號學習之訊號捕捉特性及其多個測試案例(並總結臨時遮蔽字和系統遮蔽字的作用域關係)-->(非常重要)

12訊號學習之訊號捕捉特性及其多個測試案例(並總結臨時遮蔽字和系統遮蔽字的作用域關係)-->(非常重要)

技術標籤:Linux訊號的學習linux

1 訊號捕捉特性

  • 1)程序正常執行時,預設PCB中有一個訊號遮蔽字,假定為☆,它決定了程序自動遮蔽哪些訊號。當註冊了某個訊號捕捉函式,捕捉到該訊號以後,要呼叫該函式。而該函式有可能執行很長時間,在這期間所遮蔽的訊號不由☆來指定。而是用sa_mask來指定。呼叫完訊號處理函式,再恢復為☆。
  • 2)XXX訊號捕捉函式執行期間,XXX訊號自動被遮蔽。
  • 3)阻塞的常規訊號不支援排隊,產生多次只記錄一次。(後32個實時訊號支援排隊)

本篇可以與上一篇對照理解。

https://blog.csdn.net/weixin_44517656/article/details/113067715

2 訊號捕捉特性的多個測試案例

2.1 為某個訊號設定捕捉函式
太簡單 了,直接參考下面的程式即可。

2.2 測試訊號捕捉特性的第2和3點

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>

/*自定義的訊號捕捉函式*/
void sig_int(int signo)
{
	if(signo == SIGINT){
		printf("catch you %d\n", signo);
		sleep(10);//模擬長時間執行
}else if(signo == SIGQUIT){ printf("catch you %d\n", signo); } return; } int main(void) { struct sigaction act; act.sa_handler = sig_int; sigemptyset(&act.sa_mask); //傳0遮蔽集,不遮蔽任何訊號,sa_mask只在sig_int函式有效 //sigprocmask(SIGINT, act, NULL);//注意呼叫該函式是將訊號集新增到系統的訊號集,所以要想只在回撥生效則不應該呼叫這一步
act.sa_flags = 0; //預設屬性 int ret = sigaction(SIGINT, &act, NULL); if(ret == -1){ printf("sigaction failed.\n"); return -1; } ret = sigaction(SIGQUIT, &act, NULL);//即ctrl+\ if(ret == -1){ printf("sigaction failed.\n"); return -1; } printf("------------main slept 10\n"); sleep(10); while(1);//該迴圈只是為了保證有足夠的時間來測試函式特性 return 0; }

結果分析:
1)首先我們第一次按下ctrl+c,它會先列印一次,然後睡眠十秒。
在這裡插入圖片描述
2)在睡眠十秒的過程中,我不斷按下ctrl+c,該訊號是被遮蔽了,若無遮蔽的話,應該是重新執行該回調函式列印並重新睡眠的,但結果是仍然在睡眠。所以這裡驗證了第2點。
在這裡插入圖片描述
3)當過完十秒後,我什麼也沒做,結果終端會再次列印一次,這就驗證了第三點,常規訊號多次產生,程序只會記錄一次。
在這裡插入圖片描述

2.3 案例
驗證sa_mask在捕捉函式執行期間的遮蔽作用,即在執行捕捉函式期間,新增除了自動遮蔽本訊號的其它訊號,看其是否生效,生效就證明該臨時遮蔽字只在回撥執行有效,因為系統遮蔽字是沒有遮蔽該訊號的。

/*自動遮蔽本訊號,呼叫完畢後遮蔽自動解除*/

#include <stdio.h>
#include <signal.h>
#include <unistd.h>

/*自定義的訊號捕捉函式*/
void sig_int(int signo)
{
	if(signo == SIGINT){
		printf("catch you %d\n", signo);
		sleep(10);//模擬長時間執行
	}

int main(void)
{
	struct sigaction act, old;		

	act.sa_handler = sig_int;
	sigemptyset(&act.sa_mask);
	sigaddset(&act.sa_mask, SIGQUIT);//在回撥函式執行期間除了遮蔽本訊號,也遮蔽SIGQUIT訊號。
	act.sa_flags = 0;

	int ret = sigaction(SIGINT, &act, &old);	//註冊訊號處理函式
	if(ret == -1){
		printf("sigaction failed.\n");
		return -1;
	}
	
	while(1);

	//sigaction(SIGINT, &old, NULL);	//註冊訊號處理函式

	return 0;
}

上面的結果就是第一次按下ctrl+c程式列印一次,然後睡眠,睡眠期間即回撥執行期間再按下ctrl+c或者ctrl+\程式都不會終止,因為回撥執行期間這兩個訊號被臨時遮蔽字遮蔽了。但是當程式執行完後它會儲存著一次該訊號,所以10秒之後程式自動結束,注意結束是因為觸發了3訊號的終止動作。所以我們有時候程式再執行時我們按下ctrl+c沒反應,這是因為該訊號被捕捉了,只能等待程式結束後才能執行終止動作。
在這裡插入圖片描述

3 總結臨時遮蔽字和系統遮蔽字的作用域關係

  • 1)在回撥函式執行期間,臨時遮蔽字預設只遮蔽本訊號,若想在執行期間也遮蔽其它訊號,需要提前操作sa_mask新增對應的遮蔽。並且若回撥執行期間,臨時遮蔽字並未遮蔽的訊號,由系統遮蔽字處理(生效),或者說回撥期間臨時遮蔽字比系統遮蔽字優先使用。例如我註冊了2訊號,預設回撥執行期間只會遮蔽2訊號自己,當我按下ctrl+\觸發3訊號時,由於臨時遮蔽字並未遮蔽,所以由系統遮蔽字,結果是系統將被終止。
  • 2)不在回撥函式執行期間,那就簡單了,系統遮蔽字有效。