Unix信号处理一些笔记
為什么80%的碼農(nóng)都做不了架構(gòu)師?>>> ??
從信號到線程,然后再到線程中的信號,再然后就是守護(hù)進(jìn)程中關(guān)于信號的一些個實例,一堆系統(tǒng)調(diào)用,有點迷茫,貌似又有一線線索在串起這些個知識點,嗯,就先把這些知識點寫出來,再總結(jié)吧。
1、進(jìn)程的信號屏蔽字:規(guī)定了進(jìn)程當(dāng)前要阻塞的信號集。
? ? ?sigprocmask調(diào)用就是設(shè)置當(dāng)前進(jìn)程的信號屏蔽字的,但要注意,該調(diào)用對基于線程的信號處理是無效的(后面將提及,pthread_sigmask調(diào)用才是針對線程的信號屏蔽字的設(shè)置)。
2、如果有信號被阻塞,也就是未決狀態(tài)的信號,那么可以調(diào)用sigpending取得當(dāng)前所有的未決信號集,多次發(fā)生的信號會被認(rèn)為是發(fā)生了一次,并寫入sigpending傳入的參數(shù)指針指向的內(nèi)存中。注,這時這些信號仍舊是未決狀態(tài),sigpending所做的就是查詢一下當(dāng)前有哪些信號被block掉了。
3、有什么辦法可以臨時解除某些信號的屏蔽,并等待這些信號的發(fā)生,在這些信號發(fā)生后,調(diào)用進(jìn)程停止等待,繼續(xù)執(zhí)行,并恢復(fù)原先的信號屏蔽設(shè)置呢?有的,就是sigsuspend,該調(diào)用臨時設(shè)置參數(shù)指定屏蔽字為當(dāng)前進(jìn)程的信號屏蔽字,在捕捉到一個信號或者發(fā)生了一個會終止該進(jìn)程的信號之前,該進(jìn)程被掛起,如果捕捉到一個信號而且從該信號處理程序返回,則sigsuspend返回,并且將該進(jìn)程的信號屏蔽字設(shè)置為調(diào)用sigsuspend之前的值。
下面一個例子:
void sig_usr(int signo) {// do some things about signal }int main(int argc, char *argv[]) {sigset_t global_mask;sigset_t tmp_mask;if (SIG_ERR == signal(SIGUSR1, sig_usr)) {perror("signal error");exit(1);}// block all signalssigfillset(&global_mask);sigprocmask(SIG_BLOCK, &global_mask, NULL);// unblock SIGUSR1 temporarilysigfillset(&tmp_mask);sigdelset(&tmp_mask, SIGUSR1);sigsuspend(&tmp_mask);printf("after sigsuspend.....\n");return 0; }上面的例子的意思是,屏蔽所有的信號,僅只處理SIGUSR1信號一次,注意,這里必須為SIGUSR1信號設(shè)置信號處理程序,否則,該程序就不會在sigsuspend之后返回了。sigsuspend一般都是用來做進(jìn)程同步用的,進(jìn)程間通過發(fā)送信號告訴對方它自己已經(jīng)準(zhǔn)備好了,常常是接收信號的一方要等待對方的信號,所以一般都會使用sigsuspend來等待。但是要注意了,在等待這些信號之前,就必須要先把這些信號屏蔽了,否則就會產(chǎn)生一個時間窗口,在sigsuspend之前就錯過了信號了(如果這個信號永遠(yuǎn)都不再發(fā)生,那么這個進(jìn)程就永遠(yuǎn)阻塞)。在APUE中有這樣的一個例子,就是進(jìn)程間的同步,在for之前就把信號屏蔽好了,然后再fork,各個子進(jìn)程然后再sigsuspend,這樣就會不錯過信號了。
注意,以上所講述的調(diào)用,僅只針對進(jìn)程,而在線程中則無效的。
4、線程中關(guān)于信號的機制(建議看FreeBSD的manpages,Linux的manpages是Posix定義的,太TMD暈了)
(1)每個線程都有自己的信號屏蔽字,新被創(chuàng)建的線程繼承創(chuàng)建者線程的信號屏蔽字(副本),?他們須通過pthread_sigmask來設(shè)置自己的信號屏蔽字(包括main在內(nèi))。
(2)信號處理是進(jìn)程中所有線程共享的,也就是說,任何一個線程改變了信號的處理方法,都影響其它線程。如果信號與硬件故障或計時器超時相關(guān),該信號就被發(fā)送到引起該事件的線程中去。其它信號則被發(fā)送到任意一個線程(在Linux中,就是當(dāng)前進(jìn)程的第一個線程-_-|||)。
(3)在線程處理中,與sigsuspend(進(jìn)程)相似的調(diào)用是sigwait,但是它功能就強多了,語義上也有所不同,首先,它第一個參數(shù)的意思是要等待信號集(sigsuspend則是把第一個參數(shù)信號集臨時設(shè)為當(dāng)前進(jìn)程的信號屏蔽字,意思是相反的),然后,第二個參數(shù)是取得當(dāng)前等待得到的信號(這一點,sigsuspend是做不到的)。
? ? sigwait的機制是:sigwait調(diào)用會自動取消傳入?yún)?shù)信號集的阻塞狀態(tài),直到有新的信號被遞送,而在sigwait返回之前,它會把該等待到的信號從未決的信號集中去除,并恢復(fù)線程的信號屏蔽字。(APUE中文版關(guān)于這部份的翻譯是錯的,請看英文版相關(guān)的描述。)。
? ? 如果信號被捕獲,而線而又正在用sigwait來等待該信號呢?那么這時就將由操作系統(tǒng)實現(xiàn)來決定以何種方式來遞關(guān)信號了,要么就遞送給sigwait,要么就遞送給信號處理程序,但僅只能選擇其中一種方式(在開發(fā)時,最后還是僅選擇一種方式)。而sigsuspend則必須等待信號處理程序返回。
? ? sigwait常用的用法就是,使用一個專門的線程來處理信號,而不像進(jìn)程那樣使用signal調(diào)用來設(shè)置handler。這是相當(dāng)有用的用法,進(jìn)程在一開始就屏蔽所有信號,然后建立一個線程sigwait信號,然后就是while true 和switch了。
下面有一個例子:
void *signal_handler(void *args) {sigset_t mask;int signo;// wait all signalssigfillset(&mask);while(1) {sigwait(&mask, &signo);switch (signo) {case SIGUSR1:// SIGUSR1 handlerbreak;case SIGINT:// SIGINT handlerbreak;.....default:fprintf(stderr, "unexpected signal %d\n", signo);break;}}return (void *) 0; }int main(int argc, char *argv[]) {pthread_t tid;sigset_t mask;// mask all signalssigfillset(&mask);pthread_sigmask(SIG_BLOCK, &mask, NULL);pthread_create(&tid, NULL, signal_handler, NULL);while (1) {printf("main thread: I am working......\n");sleep(1);}return 0; }5、那么線程的同步呢?哦,不應(yīng)該叫同步,而應(yīng)該叫通信。其實有另外的幾種辦法,一個是pthread_cond_broadcast,條件廣播,另一種就是利用線程sigwait,向指定線程發(fā)送信號pthread_kill。(本文未完,還欠代碼未寫,-_-||趕著洗碗去了)
轉(zhuǎn)載于:https://my.oschina.net/kut/blog/30120
總結(jié)
以上是生活随笔為你收集整理的Unix信号处理一些笔记的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Linux守护进程简介
- 下一篇: HDU_1075 What Are Yo