linux信號處理函數

在談linux信號處理函數前,有必要先聊下linux信號的機制。包含信號的作用、信號的產生、信號的阻塞等。

信號機制

信號是linux進程通信的一種方式,很多情況下,信號是由一個錯誤產生的,通知進程修改行爲,但是,也由很大一部分場景是由人爲產生信號,通知進程執行某些動作。

信號的產生

信號的產生主要有以下幾種情況:

  1. 用戶在終端(比如:鍵盤)按下某些按鍵,終端(鍵盤)驅動程序會發送信號給前臺進程。比如:ctrl+c產生SIGINT(2)信號;ctrl+\產生SIGQUIT(3)信號;ctrl+z產生SIGTSTP(20)信號。
  2. 硬件異常產生信號,硬件檢測到異常就通知內核,然後內核進程向當前用戶進程發送適當的信號。比如:用戶進程進行了除以0的操作,CPU運算單元會產生異常,內核將這個異常解釋爲SIGFPE(8)信號發送給用戶進程。比如:當前用戶進程訪問了非法內存地址,MMU會產生異常,內核將這個異常解釋爲SIGEGV(11)信號發送個用戶進程。
  3. 用戶調用某些函數(例如kill函數),就可以發送信號到另一個進程。比如:終端下,輸入kill命令發送信號給某個進程(kill命令內部也是調用kill函數實現的),如果kill命令沒有明確指定信號,默認發送的是SIGTERM(15)信號,該信號的默認處理是終止進程。
  4. 當內核檢測到某種軟件條件發生時,也會通過信號通知用戶進程。比如:鬧鐘超時信號SIGALRM(14),向讀端已經關閉的管道寫數據時會產生SIGPIPE(13)信號。

信號的阻塞

實際執行信號的處理動作稱爲信號抵達,信號從產生到抵達之間的狀態,稱爲信號未決。進程可以選擇阻塞某個信號。 被阻塞的信號產生時將保持在未決狀態,直到進程解除對此信號的阻塞,才執行抵達的動作。阻塞和忽略是不相同的,只要信號被阻塞就不會抵達,而忽略是在抵達之後可選的一種處理動作。
舉個例子:
在這裏插入圖片描述
上圖中,

  • SIGHUP(1)信號未產生過(pending=0)也未阻塞(block=0),當它抵達時執行默認處理動作。
  • SIGINT(2)信號產生過(pending=1),但正在被阻塞(block=1),所以暫時不能抵達。雖然它的處理動作是忽略,但在沒有解除阻塞之前不能忽略這個信號,因爲進程仍有機會改變處理動作之後再解除阻塞。
  • SIGQUIT(3)信號未產生過(pending=0),一旦產生SIGQUIT信號將被阻塞(block=1),它的處理動作是用戶自定義函數sighandler。

linux信號相關數據結構

相關數據結構可以直接到linux上grep -R查找,一般是在<signal.h>頭文件中。

struct sigaction

struct sigaction定義如下,鏈接

struct sigaction {
	void (*sa_handler)(int);
	void (*sa_sigaction)(int, siginfo_t *, void *);
	sigset_t sa_mask;
	int sa_flags;
	void (*sa_restorer)(void);
};

struct sigset_t

struct sigset_t就是信號集合,看代碼就知道是一個信號數組。定義如下:

# define _SIGSET_NWORDS (1024 / (8 * sizeof (unsigned long int)))
typedef struct
{
	unsigned long int __val[_SIGSET_NWORDS];
} __sigset_t;
...
typedef __sigset_t sigset_t;

linux信號相關處理函數

sigemptyset

sigemptyset
初始化set所指向的信號集,使其中所有信號的對應的bit清零,表示該信號集不包含任何有效信號。
在這裏插入圖片描述

sigfillset

sigfillset
初始化set所指向的信號集,使其中所有信號的對應bit置位,表示該信號集的有效信號包括系統支持的所有信號。
在這裏插入圖片描述

sigaddset

sigaddset
在該信號集中添加某種有效信號。
在這裏插入圖片描述

sigdelset

sigdelset
在該信號集中刪除某種有效信號。
在這裏插入圖片描述

sigismember

sigismember
是一個布爾函數,用於判斷一個信號集的有效信號中是否包含某種信號,若包含賊返回1,不包含則返回0,出錯返回-1。
在這裏插入圖片描述

sigprocmask

sigprocmask
讀取或更改進程的信號掩碼,如果成功返回0,失敗返回-1。
信號掩碼是進程當前被阻塞的信號的集合,即阻塞信號集合。
SIG_BLOCK,就是進程當前已有的阻塞信號集和set參數指定的信號集的並集。
SIG_UNBLOCK,就是進程當前已有的阻塞信號集和set參數指定的信號集的差集。
SIG_SETMASK,就是直接用set參數指定的信號集來設置進程的阻塞信號集。
在這裏插入圖片描述
在這裏插入圖片描述

sigpending

sigpending
讀取當前進程的未決信號集,通過set參數傳出,調用成功則返回0,出錯則返回-1。
在這裏插入圖片描述

舉例1

#include<stdio.h>
#include<signal.h>
#include<unistd.h>
 
void printsigset(sigset_t *set)
{
	int i;
	for(i=1;i<=31;i++){
		if(sigismember(set,i))
			putchar('Y');
		else
			putchar('N');
	}
	puts("");
}
 
int main()
{
	sigset_t s,p;			//定義兩個信號集,s和p
	sigemptyset(&s);		//初始化清空信號集s
	sigemptyset(&p);		//初始化清空信號集p
	sigaddset(&s,SIGINT);	//往信號集s中添加信號SIGINT
	sigprocmask(SIG_BLOCK,&s,NULL);	//設置阻塞信號集,把SIGINT加入阻塞信號集
	while(1)
	{
		sigpending(&p);		//獲取未決信號集,通過參數p返回
		printsigset(&p);
		sleep(1);
	}
	return 0;
}

運行結果:
在這裏插入圖片描述

信號抵達的處理函數

linux信號抵達處理函數有兩個,一個是signal,一個是sigaction。
signal各個UNIX系統版本實現不同,不符合POSIX標準,即:移植性不好。sigaction符合POSIX標準,移植性好。並且sigaction功能比signal更全更強大。
在這裏插入圖片描述

signal

sigaction

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