生活随笔
收集整理的這篇文章主要介紹了
Linux下的信号处理(转自计世网)
小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
前幾天在寫一些關(guān)于信號處理的函數(shù),弄了一下午,終于得到了結(jié)果.SIGINT信號的發(fā)送原來會發(fā)送到所有的進(jìn)程之中,導(dǎo)致了很多錯(cuò)誤,最后終于搞定.下面是計(jì)世網(wǎng)的一篇文章:希望對你也有幫助.
?
-------------------------------------------------------------------------------------------------------------------------------------------------
| 前言:這一章我們討論一下Linux下的信號處理函數(shù)。 |
| Linux下的信號可以類比于DOS下的INT或者是Windows下的事件。在有一個(gè)信號發(fā)生時(shí)候相信的信號就會發(fā)送給相應(yīng)的進(jìn)程。在Linux下的信號有以下幾個(gè)。 我們使用 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)于這些信號的詳細(xì)解釋請查看man 7 signal的輸出結(jié)果。 信號事件的發(fā)生有兩個(gè)來源:一個(gè)是硬件的原因(比如我們按下了鍵盤),一個(gè)是軟件的原因(比如我們使用系統(tǒng)函數(shù)或者是命令發(fā)出信號)。 最常用的四個(gè)發(fā)出信號的系統(tǒng)函數(shù)是kill, raise, alarm和setitimer函數(shù)。 setitimer函數(shù)我們在計(jì)時(shí)器的使用 那一章再學(xué)習(xí)。 |
| int kill(pid_t pid,int sig); |
| unisigned int alarm(unsigned int seconds); |
| kill系統(tǒng)調(diào)用負(fù)責(zé)向進(jìn)程發(fā)送信號sig。 |
| 如果pid是正數(shù),那么向信號sig被發(fā)送到進(jìn)程pid。 |
| 如果pid等于0,那么信號sig被發(fā)送到所以和pid進(jìn)程在同一個(gè)進(jìn)程組的進(jìn)程 |
| 如果pid等于-1,那么信號發(fā)給所有的進(jìn)程表中的進(jìn)程,除了最大的哪個(gè)進(jìn)程號。 |
| 如果pid由于-1,和0一樣,只是發(fā)送進(jìn)程組是-pid。 |
| 我們用最多的是第一個(gè)情況。還記得我們在守護(hù)進(jìn)程那一節(jié)的例子嗎?我們那個(gè)時(shí)候用這個(gè)函數(shù)殺死了父進(jìn)程守護(hù)進(jìn)程的創(chuàng)建 |
| raise系統(tǒng)調(diào)用向自己發(fā)送一個(gè)sig信號。我們可以用上面那個(gè)函數(shù)來實(shí)現(xiàn)這個(gè)功能的。 |
| alarm函數(shù)和時(shí)間有點(diǎn)關(guān)系了,這個(gè)函數(shù)可以在seconds秒后向自己發(fā)送一個(gè)SIGALRM信號。 下面這個(gè)函數(shù)會有什么結(jié)果呢? |
| SIGALRM的缺省操作是結(jié)束進(jìn)程,所以程序在1秒之后結(jié)束,你可以看看你的最后I值為多少,來比較一下大家的系統(tǒng)性能差異(我的是2232)。 |
| 有時(shí)候我們希望進(jìn)程正確的執(zhí)行,而不想進(jìn)程受到信號的影響,比如我們希望上面那個(gè)程序在1秒鐘之后不結(jié)束。這個(gè)時(shí)候我們就要進(jìn)行信號的操作了。 |
| 信號操作最常用的方法是信號屏蔽。信號屏蔽要用到下面的幾個(gè)函數(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)鍵的一個(gè)函數(shù)。在使用之前要先設(shè)置好信號集合set。這個(gè)函數(shù)的作用是將指定的信號集合set加入到進(jìn)程的信號阻塞集合之中去,如果提供了oset那么當(dāng)前的進(jìn)程信號阻塞集合將會保存在oset里面。參數(shù)how決定函數(shù)的操作方式。 |
| SIG_BLOCK:增加一個(gè)信號集合到當(dāng)前進(jìn)程的阻塞集合之中。 |
| SIG_UNBLOCK:從當(dāng)前的阻塞集合之中刪除一個(gè)信號集合。 |
| SIG_SETMASK:將當(dāng)前的信號集合設(shè)置為信號阻塞集合。 |
| 以一個(gè)實(shí)例來解釋使用這幾個(gè)函數(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"); |
| 程序在運(yùn)行的時(shí)候我們要使用Ctrl+C來結(jié)束。如果我們在第一計(jì)算的時(shí)候發(fā)出SIGINT信號,由于信號已經(jīng)屏蔽了,所以程序沒有反映。只有到信號被取消阻塞的時(shí)候程序才會結(jié)束。 注意我們只要發(fā)出一次SIGINT信號就可以了,因?yàn)樾盘柶帘沃皇菍⑿盘柤尤氲叫盘栕枞现?#xff0c;并沒有丟棄這個(gè)信號。一旦信號屏蔽取消了,這個(gè)信號就會發(fā)生作用。 |
| 有時(shí)候我們希望對信號作出及時(shí)的反映的,比如當(dāng)擁護(hù)按下Ctrl+C時(shí),我們不想什么事情也不做,我們想告訴用戶你的這個(gè)操作不好,請不要重試,而不是什么反映也沒有的。 這個(gè)時(shí)候我們要用到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); |
| 這個(gè)函數(shù)和結(jié)構(gòu)看起來是不是有點(diǎn)恐怖呢。不要被這個(gè)嚇著了,其實(shí)這個(gè)函數(shù)的使用相當(dāng)簡單的。我們先解釋一下各個(gè)參數(shù)的含義。 signo很簡單就是我們要處理的信號了,可以是任何的合法的信號。有兩個(gè)信號不能夠使用(SIGKILL和SIGSTOP)。 act包含我們要對這個(gè)信號進(jìn)行如何處理的信息。oact更簡單了就是以前對這個(gè)函數(shù)的處理信息了,主要用來保存信息的,一般用NULL就OK了。 |
| 信號結(jié)構(gòu)有點(diǎn)復(fù)雜。不要緊我們慢慢的學(xué)習(xí)。 |
| sa_handler是一個(gè)函數(shù)型指針,這個(gè)指針指向一個(gè)函數(shù),這個(gè)函數(shù)有一個(gè)參數(shù)。這個(gè)函數(shù)就是我們要進(jìn)行的信號操作的函數(shù)。 sa_sigaction,sa_restore和sa_handler差不多的,只是參數(shù)不同罷了。這兩個(gè)元素我們很少使用,就不管了。 |
| sa_flags用來設(shè)置信號操作的各個(gè)情況。一般設(shè)置為0好了。sa_mask我們已經(jīng)學(xué)習(xí)過了 |
| 在使用的時(shí)候我們用sa_handler指向我們的一個(gè)信號操作函數(shù),就可以了。sa_handler有兩個(gè)特殊的值:SIG_DEL和SIG_IGN。SIG_DEL是使用缺省的信號操作函數(shù),而SIG_IGN是使用忽略該信號的操作函數(shù)。 |
| 這個(gè)函數(shù)復(fù)雜,我們使用一個(gè)實(shí)例來說明。下面這個(gè)函數(shù)可以捕捉用戶的CTRL+C信號。并輸出一個(gè)提示語句。 |
| #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ù)。是因?yàn)槲覀円紤]到下面這種情況。如果我們在信號操作的時(shí)候又有一個(gè)信號發(fā)生,那么程序該如何運(yùn)行呢? 為了處理在信號處理函數(shù)運(yùn)行的時(shí)候信號的發(fā)生,我們需要設(shè)置sa_mask成員。 我們將我們要屏蔽的信號添加到sa_mask結(jié)構(gòu)當(dāng)中去,這樣這些函數(shù)在信號處理的時(shí)候就會被屏蔽掉的。 |
| 由于信號的操作和處理比較復(fù)雜,我們再介紹幾個(gè)信號操作函數(shù)。 |
| int sigsuspend(const sigset_t *sigmask); |
| pause函數(shù)很簡單,就是掛起進(jìn)程直到一個(gè)信號發(fā)生了。而sigsuspend也是掛起進(jìn)程只是在調(diào)用的時(shí)候用sigmask取代當(dāng)前的信號阻塞集合。 |
| int sigsetjmp(sigjmp_buf env,int val); |
| void siglongjmp(sigjmp_buf env,int val); |
| 還記得goto函數(shù)或者是setjmp和longjmp函數(shù)嗎。這兩個(gè)信號跳轉(zhuǎn)函數(shù)也可以實(shí)現(xiàn)程序的跳轉(zhuǎn)讓我們可以從函數(shù)之中跳轉(zhuǎn)到我們需要的地方。 |
| 由于上面幾個(gè)函數(shù),我們很少遇到,所以只是說明了一下,詳細(xì)情況請查看聯(lián)機(jī)幫助。 |
| 還記得我們在守護(hù)進(jìn)程創(chuàng)建的哪個(gè)程序嗎?守護(hù)進(jìn)程在這里我們把那個(gè)程序加強(qiáng)一下。 下面這個(gè)程序會在也可以檢查用戶的郵件。不過提供了一個(gè)開關(guān),如果用戶不想程序提示有新的郵件到來,可以向程序發(fā)送SIGUSR2信號,如果想程序提供提示可以發(fā)送SIGUSR1信號。 |
| /* Linux 的默任個(gè)人的郵箱地址是 /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ù)雜,如果你想徹底的弄清楚信號操作的各個(gè)問題,那么除了大量的練習(xí)以外還要多看聯(lián)機(jī)手冊。不過如果我們只是一般的使用的話,有了上面的幾個(gè)函數(shù)也就差不多了。 我們就介紹到這里了。 |
--------------------------------------------------------------------------------------------------------------------------------------------------
原文地址:
http://www.ccw.com.cn/htm/app/linux/develop/01_7_27_7.asp
總結(jié)
以上是生活随笔為你收集整理的Linux下的信号处理(转自计世网)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。