linux下訊號編程簡介——不可靠的訊號
( 作者:mikespook | 發布日期:2002-12-8 | 瀏覽次數:135 )
關鍵字:linux,訊號,signal()
前言:
本文章只是為了給廣大和我一樣的菜鳥一個指引。如果你是高手,或對編程毫沒有興趣。建議請不要在此浪費時間。
訊號是什嗎?其實這是一個很有意思的東西。比如說汽車行駛在路上相互鳴笛這就是訊號;同樣遇到紅燈必須停下來這也是
訊號。對應到作業系統中,假設進程就是汽車,那麼進程之間可以發送訊號;同樣如果假設作業系統是公路系統,那麼作業系統
也可以向進程發送訊號。
在linux中訊號分為不可靠(或叫不安全)的訊號與可靠的訊號。後面我會解釋為什麼這麼說。
首先,簡單介紹一下linux系統中的通用訊號:
SIGHUP 終端線以掛起。這是指當丟失載波訊號時數據機線的掛起操作。然而,當它用logout函數關閉時,也將適用於
任一終端裝置。
SIGINT 終端線已經收到中斷字元。
SIGQUIT 終端線已經收到退出字元。
SIGUSR1 使用者定義訊號1。
SIGUSR2 使用者定義訊號2。
SIGTERM 正被終止的進程。
SIGCHLD 一個子進程已經終止。
SIGPIPE 半關閉管道發生寫操作。
SIGALRM Alarm函數記時器到時。
再來看看要用的函數signal()。signal()函數的定義在標頭檔 signal.h 中,它的函數原形是 sig_t signal(int sig,
sig_t func); 第一個參數是想要註冊的訊號,第二個參數是指向訊號處理函數的指標,或預設操作。系統提供了兩種預設操作:
SIG_DEL 和 SIG_IGN,分別是預設訊號操作和忽略訊號。
好了說了一大堆枯燥無味的東西,有些暈了。單刀直入,看一個程式把。還記得上次那個多進程的程式嗎?我對其進行了一
些修改。使得當子進程運行結束後不必等父進程順序運行到 wait() 函數,而是直接轉入訊號處理函數。唉,複雜說不清楚。自
己看吧!
/*--------------------------signal_fork.c------------------------------*/
/* mikespook */
/* exercise function signal() */
/* 2002.7.18 */
#include
#include
#include
#include
#define FAC_N 65535
/* 子進程調用的函數,這裡我為了類比一個很大的後台操作用了一個迴圈。 */
void big_loop(int n);
/* 父進程調用的函數,其實不放到函數裡也可以,不過為了程式的結構更好看還是放到函數裡的好 */
void input_information();
/* 訊號處理函數,當父進程收到SIGCHLD訊號的時候就會轉入該函數進行操作 */
void handler();
int main()
{
/* 進程號 */
pid_t pid;
/* 想用訊號?那先對訊號處理函數進行註冊,對應起來 */
signal(SIGCHLD, handler);
/* 程式在這裡“分叉”,新的進程建立了 */
pid = fork();
/* 通過fork()的傳回值來判斷是父進程還是子進程 */
switch(pid){
/* 返回 -1,很不幸,建立進程失敗了。可能是沒有足夠的記憶體空間,也可能已經開起了太多的進程。 */
case -1:
perror("fork/n");
break;
/* 返回 0,現在是在子進程裡運行,那就調用子進程的操作函數。 */
case 0:
/* 一個運行65535次的迴圈,如果你的機子太快,不能看清楚兩個進程同時啟動並執行效果,那就再加大迴圈次數。或用
sleep()函數 */
big_loop(FAC_N);
/* 取得子進程的PID,你可以看清楚子進程和父進程的PID是不同的(子進程的PID比父進程的要大,因為是在父進程運行
後才建立的)。*/
printf("PID:%d/n", getpid());
/* 子進程執行完了,發送訊號 SIGCHLD。由於前面對這個訊號進行了註冊,這時就會轉入訊號處理操作。 */
break;
/* 哈哈,返回的即不是錯誤,又不是子進程,那就是父進程嘍。*/
default:
/* 這裡讓使用者輸入了4個數 */
input_information();
/* 取得子進程的PID。*/
printf("PID:%d/n", getpid());
break;
}
exit(0);
}
/*big_loop: 簡單,一看就明白,不解釋了。*/
void big_loop(int n)
{
int i;
for(i = 0; i < n; i++){
switch(i % 4){
case 0:
putchar('-');
break;
case 1:
putchar('/');
break;
case 2:
putchar('|');
break;
case 3:
putchar('//');
break;
}
putchar('/b');
}
}
/*input_information: 簡單,一看就明白,也不解釋了。*/
void input_information()
{
int n_table[4], i;
for(i = 0; i < 4; i++){
printf("Number %d:/t", i);
scanf("%d", &n_table[i]);
}
printf("Number1/tNumber2/tNumber3/tNumber4/n");
printf("%d/t%d/t%d/t%d/n", n_table[0], n_table[1], n_table[2], n_table[3]);
}
/* 訊號處理函數,當子進程結束的時候會向父進程發送 SIGCHLD 訊號,這時程式會轉入這個函數裡進行處理 */
void handler()
{
/* 父進程確認,子進程退出 */
wait();
}
/*--------------------------signal_fork.c------------------------------*/
看到了吧,訊號的使用還是很簡單的。但是這是不可靠的訊號使用方法。為什嗎?道理很簡單,當你程式啟動並執行時候收到一
個需要處理的訊號,你的程式就會轉入訊號處理函數,對訊號進行處理,處理完後再繼續執行後續程式。在這個過程中如果正好
是執行 malloc() 這樣的函數時收到了訊號,那麼當訊號處理完畢後因為 malloc() 不是一個可重新進入的函數,程式不會繼續執行
malloc() ,這樣空間也許不會分配,而造成錯誤。所以我們還需要可靠的訊號處理方式,也就是說當執行某些不可被中斷的操作
的時候屏蔽掉訊號處理。直到這些關鍵操作完成,再對訊號進行處理。以後我會給大家詳細的解釋可靠訊號的操作。
引用:http://blog.csdn.net/zhoujunyi/archive/2007/04/13/1563180.aspx