1. 程式人生 > >Linux程式設計之訊號處理

Linux程式設計之訊號處理

訊號是類Unix系統中的一種通知機制。在Linux下,我們可以通過kill -l命令來檢視有哪些訊號。Linux下有64個訊號,其中前32個訊號是經典訊號,後32個是用於驅動開發要用到的。

那麼訊號是如何產生的呢?主要有以下幾種方式可以產生訊號:

1.終端的特殊按鍵。比如說ctrl + d,ctrl + c等

2.kill 命令及函式。

3.硬體異常。如:非法訪問記憶體,除零操作。

4.某些軟條件已經發生。如:定時器alarm到時。

我們已經知道了訊號是如何產生的,那麼程序是如何對這些程序進行處理的呢?Linux系統對這些經典訊號提供了3種處理方式。

第一種是預設處理方式。在預設處理方式下,訊號有5種預設的動作,Term,Ign,Core,Stop,Cont,以下是它們的說明:

1.Term:終止當前程序

2.Ign:忽略該訊號

3.Core:終止當前程序並且產生Core檔案(用於除錯用的)

4.Stop:暫停當前程序

5.Cont:繼續執行先前暫停的程序

第二種是忽略該訊號。注意這裡的忽略和第一種情況下的Ign所在的層級是不一樣的。

我們先來看下訊號是如何響應的,在Linux的程序核心空間中,有兩個訊號集,一個是未決訊號集,只能被核心進行讀寫,使用者不能去修改它,但能去讀它。還有一個是訊號遮蔽字,這個使用者可以設定它。當有訊號產生時,未決訊號集裡剛產生的那個訊號位會被核心自動設定為1,表示該訊號產生了。接下來,如果訊號遮蔽字中對應的訊號遮蔽字位為0,說明該訊號沒有被遮蔽,可以去執行訊號的動作。若被遮蔽了,則該訊號不能被響應,直到遮蔽字位為0。當執行完該訊號的動作後,核心會將該訊號的未決訊號位設定為0。我們有幾個函式來對訊號集進行設定,sigemptyset(將訊號集全部置0),sigfillset

 (訊號集置1),sigaddset (某一位置1),sigdelset (某一位0),sigismember (判斷某一位是否為1),還有調用函式sigprocmask可以讀取或更改程序的訊號遮蔽字。

介紹了訊號集,我們來看下第三種處理方式。第三種是自定義訊號處理。我們可以通過sigaction函式來設定訊號的處理動作。該函式的第二個引數是一個名為struct sigaction 的結構體,其宣告如下:

struct sigaction {
void (*sa_handler)(int);				//訊號處理函式。早期版本的
void (*sa_sigaction)(int, siginfo_t *, void *);<span style="white-space:pre">		</span>//訊號處理函式,若使用這個就不能使用上一個,這個成員和上一個成員是互斥的,只能使用同一個
sigset_t sa_mask;<span style="white-space:pre">					</span>//臨時的訊號遮蔽字,通過該成員可以設定其它訊號的遮蔽位,執行完處理函式後,返回原有的訊號遮蔽字
int sa_flags;<span style="white-space:pre">						</span>//選擇使用哪個訊號處理函式,0表示使用早期版本的,SA_SIGINFO表示使用第二種
void (*sa_restorer)(void);<span style="white-space:pre">				</span>//保留的,沒有用
};
以下是該函式的使用示例:
#include <stdio.h>
#include <signal.h>

void do_sig(int num)
{
	//會列印該訊號的編號
	printf("num = %d\n",num);
}
int main(void)
{
	struct sigaction act;
	//設定訊號處理函式
	act.sa_handler = do_sig;

	//將臨時訊號遮蔽字全設定為0
	sigemptyset(&act.sa_mask);
	act.sa_flags = 0;
	//設定SIGINT訊號使用自定義處理該訊號
	sigaction(SIGINT,&act,NULL);
	while(1)
	{
		printf("*********\n");
		sleep(1);
	}
	return 0;
}