- 論壇徽章:
- 0
|
Linux下的信號(hào)處理函數(shù):
信號(hào)的產(chǎn)生
信號(hào)的處理
其它信號(hào)函數(shù)
一個(gè)實(shí)例
1。信號(hào)的產(chǎn)生
Linux下的信號(hào)可以類比于DOS下的INT或者是Windows下的事件.在有一個(gè)信號(hào)發(fā)生時(shí)候相信的信號(hào)就會(huì)發(fā)送給相應(yīng)的進(jìn)程.在Linux下的信號(hào)有以下幾個(gè). 我們使用 kill -l 命令可以得到以下的輸出結(jié)果:
1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL
5) SIGTRAP 6) SIGABRT 7) SIGBUS SIGFPE
9) SIGKILL 10) SIGUSR1 11) SIGSEGV 12) SIGUSR2
13) SIGPIPE 14) SIGALRM 15) SIGTERM 17) SIGCHLD
1 SIGCONT 19) SIGSTOP 20) SIGTSTP 21) SIGTTIN
22) SIGTTOU 23) SIGURG 24) SIGXCPU 25) SIGXFSZ
26) SIGVTALRM 27) SIGPROF 2 SIGWINCH 29) SIGIO
30) SIGPWR
關(guān)于這些信號(hào)的詳細(xì)解釋請(qǐng)查看man 7 signal的輸出結(jié)果. 信號(hào)事件的發(fā)生有兩個(gè)來源:一個(gè)是硬件的原因(比如我們按下了鍵盤),一個(gè)是軟件的原因(比如我們使用系統(tǒng)函數(shù)或者是命令發(fā)出信號(hào)). 最常用的四個(gè)發(fā)出信號(hào)的系統(tǒng)函數(shù)是kill, raise, alarm和setitimer函數(shù). Setitimer函數(shù)我們?cè)谟?jì)時(shí)器的使用 那一章再學(xué)習(xí).
#include
#include
#include
int kill(pid_t pid,int sig);
int raise(int sig);
unisigned int alarm(unsigned int seconds);
kill系統(tǒng)調(diào)用負(fù)責(zé)向進(jìn)程發(fā)送信號(hào)sig.
如果pid是正數(shù),那么向信號(hào)sig被發(fā)送到進(jìn)程pid.
如果pid等于0,那么信號(hào)sig被發(fā)送到所以和pid進(jìn)程在同一個(gè)進(jìn)程組的進(jìn)程
如果pid等于-1,那么信號(hào)發(fā)給所有的進(jìn)程表中的進(jìn)程,除了最大的哪個(gè)進(jìn)程號(hào).
如果pid由于-1,和0一樣,只是發(fā)送進(jìn)程組是-pid.
我們用最多的是第一個(gè)情況.還記得我們?cè)谑刈o(hù)進(jìn)程那一節(jié)的例子嗎?我們那個(gè)時(shí)候用這個(gè)函數(shù)殺死了父進(jìn)程守護(hù)進(jìn)程的創(chuàng)建
raise系統(tǒng)調(diào)用向自己發(fā)送一個(gè)sig信號(hào).我們可以用上面那個(gè)函數(shù)來實(shí)現(xiàn)這個(gè)功能的.
alarm函數(shù)和時(shí)間有點(diǎn)關(guān)系了,這個(gè)函數(shù)可以在seconds秒后向自己發(fā)送一個(gè)SIGALRM信號(hào). 下面這個(gè)函數(shù)會(huì)有什么結(jié)果呢?
#include
main()
{
unsigned int I;
alarm(1);
for(I=0;1;I++)
printf("I=%d",I);
}
SIGALRM的缺省操作是結(jié)束進(jìn)程,所以程序在1秒之后結(jié)束,你可以看看你的最后I值為多少,來比較一下大家的系統(tǒng)性能差異(我的是2232).
2。信號(hào)操作 有時(shí)候我們希望進(jìn)程正確的執(zhí)行,而不想進(jìn)程受到信號(hào)的影響,比如我們希望上面那個(gè)程序在1秒鐘之后不結(jié)束.這個(gè)時(shí)候我們就要進(jìn)行信號(hào)的操作了.
信號(hào)操作最常用的方法是信號(hào)屏蔽.信號(hào)屏蔽要用到下面的幾個(gè)函數(shù).
#include
int sigemptyset(sigset_t *set);
int sigfillset(sigset_t *set);
int sigaddset(sigset_t *set,int signo);
int sigdelset(sigset_t *set,int signo);
int sigismember(sigset_t *set,int signo);
int sigprocmask(int how,const sigset_t *set,sigset_t *oset);
sigemptyset函數(shù)初始化信號(hào)集合set,將set設(shè)置為空.sigfillset也初始化信號(hào)集合,只是將信號(hào)集合設(shè)置為所有信號(hào)的集合.sigaddset將信號(hào)signo加入到信號(hào)集合之中,sigdelset將信號(hào)從信號(hào)集合中刪除.sigismember查詢信號(hào)是否在信號(hào)集合之中.
sigprocmask是最為關(guān)鍵的一個(gè)函數(shù).在使用之前要先設(shè)置好信號(hào)集合set.這個(gè)函數(shù)的作用是將指定的信號(hào)集合set加入到進(jìn)程的信號(hào)阻塞集合之中去,如果提供了oset那么當(dāng)前的進(jìn)程信號(hào)阻塞集合將會(huì)保存在oset里面.參數(shù)how決定函數(shù)的操作方式.
SIG_BLOCK:增加一個(gè)信號(hào)集合到當(dāng)前進(jìn)程的阻塞集合之中.
SIG_UNBLOCK:從當(dāng)前的阻塞集合之中刪除一個(gè)信號(hào)集合.
SIG_SETMASK:將當(dāng)前的信號(hào)集合設(shè)置為信號(hào)阻塞集合.
以一個(gè)實(shí)例來解釋使用這幾個(gè)函數(shù).
#include
#include
#include
#include
int main(int argc,char **argv)
{
double y;
sigset_t intmask;
int I,repeat_factor;
if(argc!=2)
{
fprintf(stderr,"Usage:%s repeat_factor\n\a",argv[0]);
exit(1);
}
if((repeat_factor=atoi(argv[1]))<1)repeat_factor=10;
sigemptyset(&intmask); /* 將信號(hào)集合設(shè)置為空 */
sigaddset(&intmask,SIGINT); /* 加入中斷 Ctrl+C 信號(hào)*/
while(1)
{
/*阻塞信號(hào),我們不希望保存原來的集合所以參數(shù)為NULL*/
sigprocmask(SIG_BLOCK,&intmask,NULL);
fprintf(stderr,"SIGINT signal blocked\n" ;
for(I=0;I fprintf(stderr,"Blocked calculation is finished\n" ;
/* 取消阻塞 */
sigprocmask(SIG_UNBLOCK,&intmask,NULL);
fprintf(stderr,"SIGINT signal unblocked\n" ;
for(I=0;I fprintf(stderr,"Unblocked calculation is finished\n" ;
}
exit(0);
}
程序在運(yùn)行的時(shí)候我們要使用Ctrl+C來結(jié)束.如果我們?cè)诘谝挥?jì)算的時(shí)候發(fā)出SIGINT信號(hào),由于信號(hào)已經(jīng)屏蔽了,所以程序沒有反映.只有到信號(hào)被取消阻塞的時(shí)候程序才會(huì)結(jié)束. 注意我們只要發(fā)出一次SIGINT信號(hào)就可以了,因?yàn)樾盘?hào)屏蔽只是將信號(hào)加入到信號(hào)阻塞集合之中,并沒有丟棄這個(gè)信號(hào).一旦信號(hào)屏蔽取消了,這個(gè)信號(hào)就會(huì)發(fā)生作用.
有時(shí)候我們希望對(duì)信號(hào)作出及時(shí)的反映的,比如當(dāng)擁護(hù)按下Ctrl+C時(shí),我們不想什么事情也不做,我們想告訴用戶你的這個(gè)操作不好,請(qǐng)不要重試,而不是什么反映也沒有的. 這個(gè)時(shí)候我們要用到sigaction函數(shù).
#include
int sigaction(int signo,const struct sigaction *act,
struct sigaction *oact);
struct sigaction {
void (*sa_handler)(int signo);
void (*sa_sigaction)(int siginfo_t *info,void *act);
sigset_t sa_mask;
int sa_flags;
void (*sa_restore)(void);
}
這個(gè)函數(shù)和結(jié)構(gòu)看起來是不是有點(diǎn)恐怖呢.不要被這個(gè)嚇著了,其實(shí)這個(gè)函數(shù)的使用相當(dāng)簡單的.我們先解釋一下各個(gè)參數(shù)的含義. Signo很簡單就是我們要處理的信號(hào)了,可以是任何的合法的信號(hào).有兩個(gè)信號(hào)不能夠使用(SIGKILL和SIGSTOP). Act包含我們要對(duì)這個(gè)信號(hào)進(jìn)行如何處理的信息.oact更簡單了就是以前對(duì)這個(gè)函數(shù)的處理信息了,主要用來保存信息的,一般用NULL就OK了.
信號(hào)結(jié)構(gòu)有點(diǎn)復(fù)雜.不要緊我們慢慢的學(xué)習(xí).
sa_handler是一個(gè)函數(shù)型指針,這個(gè)指針指向一個(gè)函數(shù),這個(gè)函數(shù)有一個(gè)參數(shù).這個(gè)函數(shù)就是我們要進(jìn)行的信號(hào)操作的函數(shù). Sa_sigaction,sa_restore和sa_handler差不多的,只是參數(shù)不同罷了.這兩個(gè)元素我們很少使用,就不管了.
sa_flags用來設(shè)置信號(hào)操作的各個(gè)情況.一般設(shè)置為0好了.sa_mask我們已經(jīng)學(xué)習(xí)過了
在使用的時(shí)候我們用sa_handler指向我們的一個(gè)信號(hào)操作函數(shù),就可以了.sa_handler有兩個(gè)特殊的值:SIG_DEL和SIG_IGN.SIG_DEL是使用缺省的信號(hào)操作函數(shù),而SIG_IGN是使用忽略該信號(hào)的操作函數(shù).
這個(gè)函數(shù)復(fù)雜,我們使用一個(gè)實(shí)例來說明.下面這個(gè)函數(shù)可以捕捉用戶的CTRL+C信號(hào).并輸出一個(gè)提示語句.
#include
#include
#include
#include
#include
#define PROMPT "你想終止程序嗎?"
char *prompt=PROMPT;
void ctrl_c_op(int signo)
{
write(STDERR_FILENO,prompt,strlen(prompt));
}
int main()
{
struct sigaction act;
act.sa_handler=ctrl_c_op;
sigemptyset(&act.sa_mask);
act.sa_flags=0;
if(sigaction(SIGINT,&act,NULL)<0)
{
fprintf(stderr,"Install Signal Action Error:%s\n\a",strerror(errno));
exit(1);
}
while(1);
}
在上面程序的信號(hào)操作函數(shù)之中,我們使用了write函數(shù)而沒有使用fprintf函數(shù).是因?yàn)槲覀円紤]到下面這種情況.如果我們?cè)谛盘?hào)操作的時(shí)候又有一個(gè)信號(hào)發(fā)生,那么程序該如何運(yùn)行呢? 為了處理在信號(hào)處理函數(shù)運(yùn)行的時(shí)候信號(hào)的發(fā)生,我們需要設(shè)置sa_mask成員. 我們將我們要屏蔽的信號(hào)添加到sa_mask結(jié)構(gòu)當(dāng)中去,這樣這些函數(shù)在信號(hào)處理的時(shí)候就會(huì)被屏蔽掉的.
3。其它信號(hào)函數(shù) 由于信號(hào)的操作和處理比較復(fù)雜,我們?cè)俳榻B幾個(gè)信號(hào)操作函數(shù).
#include
#include
int pause(void);
int sigsuspend(const sigset_t *sigmask);
pause函數(shù)很簡單,就是掛起進(jìn)程直到一個(gè)信號(hào)發(fā)生了.而sigsuspend也是掛起進(jìn)程只是在調(diào)用的時(shí)候用sigmask取代當(dāng)前的信號(hào)阻塞集合.
#include
int sigsetjmp(sigjmp_buf env,int val);
void siglongjmp(sigjmp_buf env,int val);
還記得goto函數(shù)或者是setjmp和longjmp函數(shù)嗎.這兩個(gè)信號(hào)跳轉(zhuǎn)函數(shù)也可以實(shí)現(xiàn)程序的跳轉(zhuǎn)讓我們可以從函數(shù)之中跳轉(zhuǎn)到我們需要的地方.
由于上面幾個(gè)函數(shù),我們很少遇到,所以只是說明了一下,詳細(xì)情況請(qǐng)查看聯(lián)機(jī)幫助.
4。一個(gè)實(shí)例 還記得我們?cè)谑刈o(hù)進(jìn)程創(chuàng)建的哪個(gè)程序嗎?守護(hù)進(jìn)程在這里我們把那個(gè)程序加強(qiáng)一下. 下面這個(gè)程序會(huì)在也可以檢查用戶的郵件.不過提供了一個(gè)開關(guān),如果用戶不想程序提示有新的郵件到來,可以向程序發(fā)送SIGUSR2信號(hào),如果想程序提供提示可以發(fā)送SIGUSR1信號(hào).
#include
#include
#include
#include
#include
#include
#include
#include
#include
/* Linux 的默任個(gè)人的郵箱地址是 /var/spool/mail/ */
#define MAIL_DIR "/var/spool/mail/"
/* 睡眠10秒鐘 */
#define SLEEP_TIME 10
#define MAX_FILENAME 255
unsigned char notifyflag=1;
long get_file_size(const char *filename)
{
struct stat buf;
if(stat(filename,&;buf)==-1)
{
if(errno==ENOENT)return 0;
else return -1;
}
return (long)buf.st_size;
}
void send_mail_notify(void)
{
fprintf(stderr,"New mail has arrived\007\n" ;
}
void turn_on_notify(int signo)
{
notifyflag=1;
}
void turn_off_notify(int signo)
{
notifyflag=0;
}
int check_mail(const char *filename)
{
long old_mail_size,new_mail_size;
sigset_t blockset,emptyset;
sigemptyset(&;blockset);
sigemptyset(&;emptyset);
sigaddset(&;blockset,SIGUSR1);
sigaddset(&;blockset,SIGUSR2);
old_mail_size=get_file_size(filename);
if(old_mail_size<0)return 1;
if(old_mail_size>;0) send_mail_notify();
sleep(SLEEP_TIME);
while(1)
{
if(sigprocmask(SIG_BLOCK,&;blockset,NULL)<0) return 1;
while(notifyflag==0)sigsuspend(&;emptyset);
if(sigprocmask(SIG_SETMASK,&;emptyset,NULL)<0) return 1;
new_mail_size=get_file_size(filename);
if(new_mail_size>;old_mail_size)send_mail_notify;
old_mail_size=new_mail_size;
sleep(SLEEP_TIME);
}
}
int main(void)
{
char mailfile[MAX_FILENAME];
struct sigaction newact;
struct passwd *pw;
if((pw=getpwuid(getuid()))==NULL)
{
fprintf(stderr,"Get Login Name Error:%s\n\a",strerror(errno));
exit(1);
}
strcpy(mailfile,MAIL_DIR);
strcat(mailfile,pw->;pw_name);
newact.sa_handler=turn_on_notify;
newact.sa_flags=0;
sigemptyset(&;newact.sa_mask);
sigaddset(&;newact.sa_mask,SIGUSR1);
sigaddset(&;newact.sa_mask,SIGUSR2);
if(sigaction(SIGUSR1,&;newact,NULL)<0)
fprintf(stderr,"Turn On Error:%s\n\a",strerror(errno));
newact.sa_handler=turn_off_notify;
if(sigaction(SIGUSR1,&;newact,NULL)<0)
fprintf(stderr,"Turn Off Error:%s\n\a",strerror(errno));
check_mail(mailfile);
exit(0);
}
信號(hào)操作是一件非常復(fù)雜的事情,比我們想象之中的復(fù)雜程度還要復(fù)雜,如果你想徹底的弄清楚信號(hào)操作的各個(gè)問題,那么除了大量的練習(xí)以外還要多看聯(lián)機(jī)手冊(cè).不過如果我們只是一般的使用的話,有了上面的幾個(gè)函數(shù)也就差不多了. 我們就介紹到這里了. |
|