Linux下信號--阻塞信號

這篇博客http://blog.csdn.net/l_xrui/article/details/72885978講了信號的基本概念與產生方式。

瞭解以下三種概念:

信號遞達(Delivery):實際執行信號的處理動作(三種);

信號未決(Pending):信號從產生到遞達之間的狀態;

信號阻塞(Block):進程可以選擇阻塞某個信號,被阻塞的信號產生時將保持在未決狀態,直到進程解除對此信號的阻塞,才執行遞達的動作。

注意,阻塞和忽略是不同的,只要信號被阻塞就不會遞達,而忽略是在遞達之後可選的一種處理動作。


在每個進程的task_struct中,操作系統爲每個進程提供一套信號機制:


以上可知分別有三個表:阻塞(block)表,未決(pending)表,遞達表

block表與pending表在task_struct中其是以相同的數據類型sigset_t來存儲,sigset_t稱爲信號集,這個類型變量對應每一bit位可以表示對應每個信號的“有效(1)”或“無效(0)”狀態,在阻塞信號集(block)中“有效”和“無效”的含義是該信號是否被阻塞,而在未決信號集中“有效”和“無效”的含義是該信是否處於未決狀態;

遞達表則是以數組來表示,數組下標表示哪一個信號,數組裏內容爲函數指針,指向對應信號的遞達動作。

每個信號都有兩個標誌位分別表示阻塞(block)和未決(pending),還有一個函數指針表示處理動作。信號產生時,內核在進程控制塊中設置該信號的未決標誌,直到信號遞達才清除該標誌,若該信號被阻塞,信號產生時,一直處在未決狀態,直至信號被取消阻塞。

如果在進程解除對某信號的阻塞之前這種信號產生過多次,Linux處理機制是:常規信號在遞達之前產生多次只計一次,而實時信號在遞達之前產生多次可以依次放在一個隊列裏記錄。

由此也可描述上圖:

(1).  1號信號未被阻塞也未產生,它的遞達動作爲忽略;

(2).  2號信號被阻塞並已產生,所以一直在未決狀態,它的遞達動作爲自定義處理動作,當其被取消阻塞,則會在適當時間執行其遞達動作;

(3).  3號信號被阻塞但未產生,其遞達動作爲默認,但當它產生因被阻塞也會一直在阻塞狀態。


由上一篇博客可知指針非法操作進程異常退出可這樣描述:

在進程運行中,非法指針地址通過頁表映射時不可映射訪問被MMU發現,此時操作系統發現MMU出錯,則查找出錯誤原因發送正確信號(SIGSEGV)11號信號給進程,即改變進程pending表的11位改爲有效狀態(1),然後進程發現自己pending表發生改變,接收到信號,其再去查詢block表11號信號是否被阻塞,沒有被阻塞,則信號遞達,信號pending表恢復無效(0),進程就去找到hander表保存的對應的函數地址,去執行了默認動作,最後退出。


信號集操作函數:

使用者只能調用以下函數來操作sigset_t變量:

#include <signal.h>
int sigemptyset(sigset_t *set);   //初始化set所指向的信號集,使其中所有信號的對應bit清零
int sigfillset(sigset_t *set);  //初始化set所指向的信號集,使其中所有信號的對應bit置有效1位
int sigaddset(sigset_t *set, int signo); //向信號集set中添加使signo位信號有效
int sigdelset(sigset_t *set, int signo);  //向信號集set中刪除使signo位信號無效
int sigismember(const sigset_t *set, int signo);  //判斷信號集set的有效信號中是否包含signo信號,若包含則返回1,不包含則返回0,出錯返回-1

調用函數sigprocmask可以讀取或更改進程的信號屏蔽字(阻塞信號集):

int sigprocmask(int how, const sigset_t *set, sigset_t *oset);
參數:

oset:若set是空指針,oset是非空指針,則讀取進程的當前信號屏蔽字通過oset參數傳出;

         若oset和set都是非空指針,則先將原來的信號屏蔽字備份到oset裏,然後根據set和how參數更改信號屏蔽字;

set:若set是非空指針,則更改進程的信號屏蔽字;

how:如何修改,可選值如下:

1. SIG_BLOCK  //將set信號集中的有效信號添加到當前阻塞表中阻塞它們
2. SIG_UNBLOCK  //將set信號集中的有效信號從當前阻塞表中刪除對它們取消阻塞
3.SIG_SETMASK  //將當前阻塞表清空只將set信號集中的有效信號添加進去阻塞它們
返回值:若成功則爲0,若出錯則爲-1

注意:如果調用sigprocmask解除了對當前若干個未決信號的阻塞,則在sigprocmask返回前,至少將其中一個信號遞達。


函數:

int sigpending(sigset_t *set);  //sigpending讀取當前進程的未決信號集,通過set參數傳出
返回值:調用成功則返回0,出錯則返回-1


以下用代碼驗證三張表以上關係:

sigblock.c:此處函數實現功能以2號信號(Ctrl-C)舉例:

(1)設置信號集將2號信號加入信號集中,並調用函數sigprocmask()將其阻塞,併爲2號信號設自定義信號捕捉函數(打印當前進程pid與接收到的信號sig);

(2)設一個計數器count,進入無限循環,每隔一秒打印一次block表與pending表,並使count++;

(3)在無限循環中,當count==10時,再調用函數sigprocmask()對2號信號取消阻塞。

#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <sys/types.h>

//信號遞達動作 
void myhander(int sig)
{
	printf("pid## %d receiving sig## %d\n",getpid(),sig);
}
// 打印pending表(信號未決) 
void PrintPending(sigset_t set)
{
	int i=1;
	for(;i<32;++i)
	{
		if(sigismember(&set,i))
		{
			printf("1 ");
		}
		else
		{
			printf("0 ");
		}
	}
	printf("\n");
}
int main()
{
	//定義信號集 
	sigset_t s;
	//初始化信號集爲0 
	sigemptyset(&s);
	//將2號信號加入信號集中:ctrl c==2
	sigaddset(&s,2);

	sigset_t oldset;    //以保存block表中舊的信號屏蔽字 
	//sigprocmask(SIG_BLOCK,&s,&oldset);     //將S信號集中信號加入block(阻塞表)中 :此處阻塞了2號信號 
	sigprocmask(SIG_SETMASK,&s,&oldset);     //將block表中阻塞信號清空只將S信號集中的信號屏蔽(設爲S的值) 
	
	signal(2,myhander);   //給2號信號設捕捉函數 
	int count=0;
	while(1)
	{
		//獲取當前阻塞(block)表,並打印 
		sigset_t s1;
		sigprocmask(0,NULL,&s1);
		printf("block list:");
		PrintPending(s1);
		
		//定義 pending表 
		sigset_t p;
		sigpending(&p);   //獲取當前進程的pending表(即進程收到哪些信號並處在未決狀態) 
		printf("pend  list:");
		PrintPending(p);   //打印查看pending表 

		if(count==10)
		{
			//sigprocmask(SIG_UNBLOCK,&s,&oldset);   //從block表中刪除信號集S中的信號,即取消它們的阻塞 
			sigprocmask(SIG_SETMASK,&oldset,&s);    //此代碼中以此可實現同樣效果 :以上oldset保存的爲全0 
		}
		sleep(1);

		++count;
	}

	return 0;
}
效果如下:

(1)在前10秒count<=10時,2號信號被阻塞,此時block表與pending表依次爲:

0 1 0 0 0 0 0 0 0 0.........

0 0 0 0 0 0 0 0 0 0.........

在前10秒因爲2號信號被阻塞,若這時鍵盤發送Ctrl-C,2號信號不會遞達(不執行自定義捕捉函數),其會先保持在未決狀態,此時block表與pending表依次爲:

0 1 0 0 0 0 0 0 0 0.........

0 1 0 0 0 0 0 0 0 0.........

(2)當剛過10秒,count==10,2號信號被取消阻塞,這時(1)剛纔發送的2號信號遞達,則執行自定義捕捉函數,並且block表與pending表(2號信號不在未決狀態恢復爲0)依次變爲:

0 0 0 0 0 0 0 0 0 0.........

0 0 0 0 0 0 0 0 0 0.........

(3)在以後,2號信號再沒被阻塞,此時在發送則執行自定義捕捉函數,並且block表與pending表保持爲:

0 0 0 0 0 0 0 0 0 0.........

0 0 0 0 0 0 0 0 0 0.........











發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章