1. signal信號處理機制
可以用函數signal註冊一個信號捕捉函數。原型爲:
#include
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
signal的第1個參數signum表示要捕捉的信號,第2個參數是個函數指針,表示要對該信號進行捕捉的函數,該
參數也可以是 SIG_DEF(表示交由系統缺省處理,相當於白註冊了)或SIG_IGN(表示忽略掉該信號而不做任何處
理)。signal如果調用成功,返回以前該信 號的處理函數的地址,否則返回SIG_ERR。
sighandler_t是信號捕捉函數,由signal函數註冊,註冊以後,在整個進程運行過程中均有效,並且對不
同的信號可以註冊同一個信號捕捉函數。該函數只有一個參數,表示信號值。
示例:
捕捉終端CTRL+c產生的SIGINT信號:
#include
#include
#include
#include
void SignHandler(int iSignNo)
{
printf("Capture sign no:%d\n",iSignNo);
}
int main()
{
signal(SIGINT,SignHandler);
while(true)
sleep(1);
return 0;
}
該程序運行起來以後,通過按CTRL+c將不再終止程序的運行。應爲CTRL+c產生的SIGINT信號已經由進程中注
冊的SignHandler函數捕 捉了。該程序可以通過Ctrl+\終止,因爲組合鍵Ctrl+\能夠產生SIGQUIT信號,而該信
號的捕捉函數尚未在程序中註冊。
忽略掉終端CTRL+c產生的SIGINT信號:
#include
#include
#include
#include
int main()
{
signal(SIGINT,SIG_IGN);
while(true)
sleep(1);
return 0;
}
該程序運行起來以後,將CTRL+C產生的SIGINT信號忽略掉了,所以CTRL+C將不再能是該進程終止,要終止
該進程,可以向進程發送SIGQUIT信號,即組合鍵CTRL+\
接受信號的默認處理,接受默認處理就相當於沒有寫信號處理程序:
#include
#include
#include
#include
int main()
{
signal(SIGINT,DEF);
while(true)
sleep(1);
return 0;
}
2. sigaction信號處理機制
2.1. 信號處理情況分析
在signal處理機制下,還有許多特殊情況需要考慮:
冊一個信號處理函數,並且處理完畢一個信號之後,是否需要重新註冊,才能夠捕捉下一個信號;
如果信號處理函數正在處理信號,並且還沒有處理完畢時,又發生了一個同類型的信號,這時該怎麼處理;
如果信號處理函數正在處理信號,並且還沒有處理完畢時,又發生了一個不同類型的信號,這時該怎麼處理;
如果程序阻塞在一個系統調用(如read(…))時,發生了一個信號,這時是讓系統調用返回錯誤再接着進入信號處
理函數,還是先跳轉到信號處理函數,等信號處理完畢後,系統調用再返回。
示例:
#include
#include
#include
#include
int g_iSeq=0;
void SignHandler(int iSignNo)
{
int iSeq=g_iSeq++;
printf("%d Enter SignHandler,signo:%d.\n",iSeq,iSignNo);
sleep(3);
printf("%d Leave SignHandler,signo:%d\n",iSeq,iSignNo);
}
int main()
{
char szBuf[8];
int iRet;
signal(SIGINT,SignHandler);
signal(SIGQUIT,SignHandler);
do{
iRet=read(STDIN_FILENO,szBuf,sizeof(szBuf)-1);
if(iRet<0){
perror("read fail.");
break;
}
szBuf[iRet]=0;
printf("Get: %s",szBuf);
}while(strcmp(szBuf,"quit\n")!=0);
return 0;
}
程序運行時,針對於如下幾種輸入情況(要輸入得快),看輸出結果:
CTRL+c] [CTRL+c] [CTRL+c]
[CTRL+c] [CTRL+\]
hello [CTRL+\] [Enter]
[CTRL+\] hello [Enter]
hel [CTRL+\] lo[Enter]
針對於上面各種情況,不同版本OS可能有不同的響應結果。
2.2. sigaction信號處理註冊
如果要想用程序控制上述各種情況的響應結果,就必須採用新的信號捕獲機制,即使用sigaction信號處理機
制。
函數原型:
#include
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
sigaction也用於註冊一個信號處理函數。
參數signum爲需要捕捉的信號;
參數 act是一個結構體,裏面包含信號處理函數地址、處理方式等信息。
參數oldact是一個傳出參數,sigaction函數調用成功後,oldact裏面包含以前對signum的處理方式的信息。
如果函數調用成功,將返回0,否則返回-1
結構體 struct sigaction(注意名稱與函數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); //保留,不要使用。
}
該結構體的各字段含義及使用方式:
1、字段sa_handler是一個函數指針,用於指向原型爲void handler(int)的信號處理函數地址, 即老類型 的信
號處理函數;
2、字段sa_sigaction也是一個函數指針,用於指向原型爲:
void handler(int iSignNum,siginfo_t *pSignInfo,void *pReserved);
的信號處理函數,即新類型的信號處理函數。
該函數的三個參數含義爲:
iSignNum:傳入的信號
pSignInfo:與該信號相關的一些信息,它是個結構體
pReserved:保留,現沒用
3、字段sa_handler和sa_sigaction只應該有一個生效,如果想採用老的信號處理機制,就應該讓sa_handler
指向正確的信號處 理函數;否則應該讓sa_sigaction指向正確的信號處理函數,並且讓字段sa_flags包含
SA_SIGINFO選項。
4、字段sa_mask是一個包含信號集合的結構體,該結構體內的信號表示在進行信號處理時,將要被阻塞的信
號。針對sigset_t結構體,有一組專門的函數對它進行處理,它們是:
#include
int sigemptyset(sigset_t *set); //清空信號集合set
int sigfillset(sigset_t *set); //將所有信號填充進set中
int sigaddset(sigset_t *set, int signum); //往set中添加信號signum
int sigdelset(sigset_t *set, int signum); //從set中移除信號signum
int sigismember(const sigset_t *set, int signum); //判斷signnum是不是包含在set中
例如,如果打算在處理信號SIGINT時,只阻塞對SIGQUIT信號的處理,可以用如下種方法:
struct sigaction act;
sigemptyset(&act.sa_mask);
sigaddset(&act_sa_mask,SIGQUIT);
sigaction(SIGINT,&act,NULL);
字段sa_flags是一組掩碼的合成值,指示信號處理時所應該採取的一些行爲,各掩碼的含義爲:
掩碼 描述
SA_RESETHAND 處理完畢要捕捉的信號後,將自動撤消信號處理函數的註冊,即必須再重新註冊信號處理函
數,才能繼續處理接下來產生的信號。該選項不符合一般的信號處理流程,現已經被廢棄。
SA_NODEFER 在處理信號時,如果又發生了其它的信號,則立即進入其它信號的處理,等其它信號處理完畢
後,再繼續處理當前的信號,即遞規地處理。如果sa_flags包含了該掩碼,則結構體sigaction的sa_mask將無
效!
SA_RESTART 如果在發生信號時,程序正阻塞在某個系統調用,例如調用read()函數,則在處理完畢信號後,
接着從阻塞的系統返回。該掩碼符合普通的程序處理流程,所以一般來說,應該設置該掩碼,否則信號處理完
後,阻塞的系統調用將會返回失敗!
SA_SIGINFO 指示結構體的信號處理函數指針是哪個有效,如果sa_flags包含該掩碼,則sa_sigactiion指針有
效,否則是sa_handler指針有效。