1. 程式人生 > >Linux下Signal訊號系統呼叫

Linux下Signal訊號系統呼叫

前面兩節已經介紹了有關訊號的大部分知 識。這一節我們來了解一下這些系統呼叫。其中,系統呼叫signal是程序用來設定某個訊號的處理方法,系統呼叫kill是用來發送訊號給指定程序的。這 兩個呼叫可以形成訊號的基本操作。後兩個呼叫pause和alarm是通過訊號實現的程序暫停和定時器,呼叫alarm是通過訊號通知程序定時器到時。所 以在這裡,我們還要介紹這兩個呼叫。 

1、signal 系統呼叫 
系統呼叫signal用來設定某個訊號的處理方法。該呼叫宣告的格式如下: 
void (*signal(int signum, void (*handler)(int)))(int); 
在使用該呼叫的程序中加入以下標頭檔案: 


#include 

上述宣告格式比較複雜,如果不清楚如何使用,也可以通過下面這種型別定義的格式來使用(POSIX的定義): 

typedef void (*sighandler_t)(int); 
sighandler_t signal(int signum, sighandler_t handler); 
但這種格式在不同的系統中有不同的型別定義,所以要使用這種格式,最好還是參考一下聯機手冊。

在呼叫中,引數signum指出要設定處理方法的訊號。第二個引數handler是一個處理函式,或者是 
SIG_IGN:忽略引數signum所指的訊號。 
SIG_DFL:恢復引數signum所指訊號的處理方法為預設值。 


傳遞給訊號處理例程的整數引數是訊號值,這樣可以使得一個訊號處理例程處理多個訊號。系統呼叫signal返回值是指定訊號signum前一次的處理例程或者錯誤時返回錯誤程式碼SIG_ERR。下面來看一個簡單的例子: 

#include 
#include 
#include 
void sigroutine(int dunno) { /* 訊號處理例程,其中dunno將會得到訊號的值 */ 
	switch (dunno) { 
		case 1: 
			printf("Get a signal -- SIGHUP "); 
			brea在使用這兩個呼叫的程序中加入以下標頭檔案: 
			#include 在使用這兩個呼叫的程序中加入以下標頭檔案: 
			#include k; 
		case 2: 
			printf("Get a signal -- SIGINT "); 
		break; 
		case 3: 
			printf("Get a signal -- SIGQUIT "); 
		break; 
	} 
	return; 
} 


int main() { 
	printf("process id is %d ",getpid()); 
	signal(SIGHUP, sigroutine); //* 下面設定三個訊號的處理方法 
	signal(SIGINT, sigroutine); 
	signal(SIGQUIT, sigroutine); 
	for (;;) ; 
} 


其中訊號SIGINT由按下Ctrl-C發出,訊號SIGQUIT由按下Ctrl-發出。該程式執行的結果如下: 

localhost:~$ ./sig_test 
process id is 463 
Get a signal -SIGINT //按下Ctrl-C得到的結果 
Get a signal -SIGQUIT //按下Ctrl-得到的結果 
//按下Ctrl-z將程序置於後臺 
[1]+ Stopped ./sig_test 
localhost:~$ bg 
[1]+ ./sig_test & 
localhost:~$ kill -HUP 463 //向程序傳送SIGHUP訊號 
localhost:~$ Get a signal – SIGHUP 
kill -9 463 //向程序傳送SIGKILL訊號,終止程序 
localhost:~$ 


2、kill 系統呼叫 
系統呼叫kill用來向程序傳送一個訊號。該呼叫宣告的格式如下: 
int kill(pid_t pid, int sig); 
在使用該呼叫的程序中加入以下標頭檔案: 
#include 
#include 

該 系統呼叫可以用來向任何程序或程序組傳送任何訊號。如果引數pid是正數,那麼該呼叫將訊號sig傳送到程序號為pid的程序。如果pid等於0,那麼信 號sig將傳送給當前程序所屬程序組裡的所有程序。如果引數pid等於-1,訊號sig將傳送給除了程序1和自身以外的所有程序。如果引數pid小於- 1,訊號sig將傳送給屬於程序組-pid的所有程序。如果引數sig為0,將不傳送訊號。該呼叫執行成功時,返回值為0;錯誤時,返回-1,並設定相應 的錯誤程式碼errno。下面是一些可能返回的錯誤程式碼: 
EINVAL:指定的訊號sig無效。 
ESRCH:引數pid指定的程序或程序組不存在。注意,在程序表項中存在的程序,可能是一個還沒有被wait收回,但已經終止執行的僵死程序。 
EPERM: 程序沒有權力將這個訊號傳送到指定接收訊號的程序。因為,一個程序被允許將訊號傳送到程序pid時,必須擁有root權力,或者是發出呼叫的程序的UID 或EUID與指定接收的程序的UID或儲存使用者ID(savedset-user-ID)相同。如果引數pid小於-1,即該訊號傳送給一個組,則該錯誤 表示組中有成員程序不能接收該訊號。 

3、pause系統呼叫 
系統呼叫pause的作用是等待一個訊號。該呼叫的宣告格式如下: 
int pause(void); 
在使用該呼叫的程序中加入以下標頭檔案: 
#include 

該呼叫使得發出呼叫的程序進入睡眠,直到接收到一個訊號為止。該呼叫總是返回-1,並設定錯誤程式碼為EINTR(接收到一個訊號)。下面是一個簡單的範例: 

#include 
#include 
#include 
void sigroutine(int unused) { 
printf("Catch a signal SIGINT "); 
} 

int main() { 
signal(SIGINT, sigroutine); 
pause(); 
printf("receive a signal "); 
} 
在這個例子中,程式開始執行,就象進入了死迴圈一樣,這是因為程序正在等待訊號,當我們按下Ctrl-C時,訊號被捕捉,並且使得pause退出等待狀態。 

4、alarm和 setitimer系統呼叫 
系統呼叫alarm的功能是設定一個定時器,當定時器計時到達時,將發出一個訊號給程序。該呼叫的宣告格式如下: 
unsigned int alarm(unsigned int seconds); 
在使用該呼叫的程序中加入以下標頭檔案: 
#include 

系 統呼叫alarm安排核心為呼叫程序在指定的seconds秒後發出一個SIGALRM的訊號。如果指定的引數seconds為0,則不再發送 SIGALRM訊號。後一次設定將取消前一次的設定。該呼叫返回值為上次定時呼叫到傳送之間剩餘的時間,或者因為沒有前一次定時呼叫而返回0。 

注意,在使用時,alarm只設定為傳送一次訊號,如果要多次傳送,就要多次使用alarm呼叫。 

對於alarm,這裡不再舉例。現在的系統中很多程式不再使用alarm呼叫,而是使用setitimer呼叫來設定定時器,用getitimer來得到定時器的狀態,這兩個呼叫的宣告格式如下: 

int getitimer(int which, struct itimerval *value); 
int setitimer(int which, const struct itimerval *value, struct itimerval *ovalue); 

在使用這兩個呼叫的程序中加入以下標頭檔案: 

#include 

該系統呼叫給程序提供了三個定時器,它們各自有其獨有的計時域,當其中任何一個到達,就傳送一個相應的訊號給程序,並使得計時器重新開始。三個計時器由引數which指定,如下所示: 
TIMER_REAL:按實際時間計時,計時到達將給程序傳送SIGALRM訊號。 
ITIMER_VIRTUAL:僅當程序執行時才進行計時。計時到達將傳送SIGVTALRM訊號給程序。 
ITIMER_PROF:當程序執行時和系統為該程序執行動作時都計時。與ITIMER_VIR-TUAL是一對,該定時器經常用來統計程序在使用者態和核心態花費的時間。計時到達將傳送SIGPROF訊號給程序。 

定時器中的引數value用來指明定時器的時間,其結構如下: 

struct itimerval { 
struct timeval it_interval; /* 下一次的取值 */ 
struct timeval it_value; /* 本次的設定值 */ 
}; 

該結構中timeval結構定義如下:

struct timeval { 
long tv_sec; /* 秒 */ 
long tv_usec; /* 微秒,1秒 = 1000000 微秒*/ 
}; 
在setitimer 呼叫中,引數ovalue如果不為空,則其中保留的是上次呼叫設定的值。定時器將it_value遞減到0時,產生一個訊號,並將it_value的值設 定為it_interval的值,然後重新開始計時,如此往復。當it_value設定為0時,計時器停止,或者當它計時到期,而it_interval 為0時停止。呼叫成功時,返回0;錯誤時,返回-1,並設定相應的錯誤程式碼errno: 
EFAULT:引數value或ovalue是無效的指標。 
EINVAL:引數which不是ITIMER_REAL、ITIMER_VIRT或ITIMER_PROF中的一個。

下面是關於setitimer呼叫的一個簡單示範,在該例子中,每隔一秒發出一個SIGALRM,每隔0.5秒發出一個SIGVTALRM訊號: 

#include 
#include 
#include 
#include 
int sec; 


void sigroutine(int signo) { 
	switch (signo) { 
		case SIGALRM: 
			printf("Catch a signal -- SIGALRM "); 
		break; 
		case SIGVTALRM: 
			printf("Catch a signal -- SIGVTALRM "); 
		break; 
	} 
	return; 
} 


int main() { 
	struct itimerval value,ovalue,value2; 
	sec = 5; 


	printf("process id is %d ",getpid()); 
	signal(SIGALRM, sigroutine); 
	signal(SIGVTALRM, sigroutine); 


	value.it_value.tv_sec = 1; 
	value.it_value.tv_usec = 0; 
	value.it_interval.tv_sec = 1; 
	value.it_interval.tv_usec = 0; 
	setitimer(ITIMER_REAL, &value, &ovalue); 


	value2.it_value.tv_sec = 0; 
	value2.it_value.tv_usec = 500000; 
	value2.it_interval.tv_sec = 0; 
	value2.it_interval.tv_usec = 500000; 
	setitimer(ITIMER_VIRTUAL, &value2, &ovalue); 


	for (;;) ; 
} 



該例子的螢幕拷貝如下:

localhost:~$ ./timer_test 
process id is 579 
Catch a signal – SIGVTALRM 
Catch a signal – SIGALRM 
Catch a signal – SIGVTALRM 
Catch a signal – SIGVTALRM 
Catch a signal – SIGALRM 
Catch a signal –GVTALRM