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