每個(gè)線程都有自己的信號(hào)屏蔽字,但是信號(hào)的處理是進(jìn)程中所有線程共享的。這意味著盡管單個(gè)線程可以阻止某些信號(hào),但當(dāng)線程修改了與某個(gè)信號(hào)相關(guān)的處理行為以后,所有的線程都必須共享這個(gè)處理行為的改變。
進(jìn)程中的信號(hào)是遞送到單個(gè)線程的。如果信號(hào)與硬件故障或計(jì)時(shí)器超時(shí)相關(guān),該信號(hào)就被發(fā)送到引起該事件的線程中去,而其他的信號(hào)則被發(fā)送到任意一個(gè)線程。
http://www.CUOXin.com/nufangrensheng/p/3515257.html中討論了進(jìn)程如何使用sigPRocmask來(lái)阻止信號(hào)發(fā)送。sigpromask的行為在多線程的進(jìn)程中并沒(méi)有定義,線程必須使用pthread_sigmask。
#include <signal.h>int pthread_sigmask(int how, const sigset_t *restrict set, sigset_t *restrict oset);返回值:若成功則返回0,否則返回錯(cuò)誤編號(hào)
pthread_sigmask函數(shù)與sigprocmask函數(shù)基本相同,除了pthread_sigmask工作在線程中,并且失敗時(shí)返回錯(cuò)誤碼,而不像sigprocmask中那樣設(shè)置errno并返回-1。
線程可以通過(guò)調(diào)用sigwait等待一個(gè)或多個(gè)信號(hào)發(fā)生。
#include <signal.h>int sigwait(const sigset_t *restrict set, int *restrict signop);返回值:若成功則返回0,否則返回錯(cuò)誤編號(hào)
set參數(shù)指出了線程等待的信號(hào)集,signop指向的整數(shù)將作為返回值,表明信號(hào)編號(hào)。
如果信號(hào)集中的某個(gè)信號(hào)在sigwait調(diào)用的時(shí)候處于未決狀態(tài),那么sigwait將無(wú)阻塞地返回,在返回之前,sigwait將從進(jìn)程中移除那些處于未決狀態(tài)的信號(hào)。為了避免錯(cuò)誤動(dòng)作發(fā)生,線程在調(diào)用sigwait之前,必須阻塞那些它正在等待的信號(hào)。sigwait函數(shù)會(huì)自動(dòng)取消信號(hào)集的阻塞狀態(tài),直到有新的信號(hào)被遞送。在返回之前,sigwait將恢復(fù)線程的信號(hào)屏蔽字。如果信號(hào)在sigwait調(diào)用的時(shí)候沒(méi)有被阻塞,在完成對(duì)sigwait調(diào)用之前會(huì)出現(xiàn)一個(gè)時(shí)間窗,在這個(gè)時(shí)間窗口期,某個(gè)信號(hào)可能在完成sigwait調(diào)用之前就被遞送了。
使用sigwait的好處在于它可以簡(jiǎn)化信號(hào)處理,允許把異步產(chǎn)生的信號(hào)用同步的方式處理。為了防止信號(hào)中斷線程,可以把信號(hào)加到每個(gè)線程的信號(hào)屏蔽字中,然后安排專用線程作信號(hào)處理。這些專用線程可以進(jìn)行函數(shù)調(diào)用,不需要擔(dān)心在信號(hào)處理程序中調(diào)用哪些函數(shù)是安全的,因?yàn)檫@些函數(shù)調(diào)用來(lái)自正常的線程環(huán)境,而非傳統(tǒng)的信號(hào)處理程序,傳統(tǒng)信號(hào)處理程序通常會(huì)中斷線程的正常執(zhí)行。
如果多個(gè)線程在sigwait調(diào)用時(shí),等待的是同一個(gè)信號(hào),這時(shí)就會(huì)出現(xiàn)線程阻塞。當(dāng)信號(hào)遞送的時(shí)候,只有一個(gè)線程可以從sigwait中返回。如果信號(hào)被捕獲(例如進(jìn)程通過(guò)使用sigaction建立了一個(gè)信號(hào)處理函數(shù)),而且線程正在sigwait調(diào)用中等待同一個(gè)信號(hào),那么這時(shí)將由操作系統(tǒng)實(shí)現(xiàn)來(lái)決定以何種方式遞送信號(hào)。在這種情況下,操作系統(tǒng)實(shí)現(xiàn)可以讓sigwait返回,也可以激活信號(hào)處理程序,但不可能出現(xiàn)兩者皆可的情況。
要把信號(hào)發(fā)送到進(jìn)程,可以調(diào)用kill(見(jiàn)http://www.CUOXin.com/nufangrensheng/p/3514817.html);要把信號(hào)發(fā)送到線程,可以調(diào)用pthread_kill。
#include <signal.h>int pthread_kill(pthread_t thread, int signo);返回值:若成功則返回0,否則返回錯(cuò)誤編號(hào)
可以傳一個(gè)0值的signo來(lái)檢查線程是否存在。如果信號(hào)的默認(rèn)處理動(dòng)作是終止該進(jìn)程,那么把信號(hào)傳遞給某個(gè)線程仍然會(huì)殺掉整個(gè)進(jìn)程。
注意鬧鐘定時(shí)器是進(jìn)程資源,并且所有的線程共享相同的alarm。所以進(jìn)程中的多個(gè)線程不可能互不干擾(或互不合作)地使用鬧鐘定時(shí)器。
實(shí)例
回憶程序清單10-16(http://www.CUOXin.com/nufangrensheng/p/3516427.html),等待信號(hào)處理程序設(shè)置標(biāo)志,從而表明主程序應(yīng)該退出。唯一可運(yùn)行的控制線程就是主線程和信號(hào)處理程序,所以阻塞信號(hào)足以避免錯(cuò)失標(biāo)志修改。在線程中,需要使用互斥量來(lái)保護(hù)標(biāo)志,如程序清單12-6所示。
程序清單12-6 同步信號(hào)處理
#include "apue.h"#include <pthread.h>int quitflag; /* set nonzero by thread */sigset_t mask;pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;pthread_cond_t wait = PTHREAD_COND_INITIALIZER;void *thr_fn(void *arg){ int err, signo; for(; ;) { err = sigwait(&mask, &signo); if(err != 0) err_exit(err, "sigwait failed"); switch(signo) { case SIGINT: printf("/ninterrupt/n"); break; case SIGQUIT: pthread_mutex_lock(&lock); quitflag = 1; pthread_mutex_unlock(&lock); pthread_cond_signal(&wait); return(0); default: printf("unexpected signal %d/n", signo); exit(1); } }}int main(void){ int err; sigset_t oldmask; pthread_t tid; sigemptyset(&mask); sigaddset(&mask, SIGINT); sigaddset(&mask, SIGQUIT); if((err = pthread_sigmask(SIG_BLOCK, &mask, &oldmask)) != 0) err_exit(err, "SIG_BLOCK error"); err = pthread_create(&tid, NULL, thr_fn, 0); if(err != 0) err_exit(err, "can't create thread"); pthread_mutex_lock(&lock); while(quitflag == 0) pthread_cond_wait(&wait, &lock); pthread_mutex_unlock(&lock); /* SIGQUIT has been caught and is now blocked; do whatever */ quitflag = 0; /* reset signal mask which unblocks SIGQUIT */ if(sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0) err_sys("SIG_SETMASK error"); exit(0);}
這里并不讓信號(hào)處理程序中斷主控線程,而是由專門的獨(dú)立控制線程進(jìn)行信號(hào)處理。改動(dòng)quitflag的值是在互斥量的保護(hù)下進(jìn)行的,這樣主控線程不會(huì)在調(diào)用pthread_cond_signal時(shí)錯(cuò)失喚醒調(diào)用。在主控線程中使用相同的互斥量來(lái)檢查標(biāo)志的值,并且原子地釋放互斥量,等待條件的發(fā)生。
注意在主線程開始時(shí)阻塞SIGINT和SIGQUIT。當(dāng)創(chuàng)建線程進(jìn)行信號(hào)處理時(shí),新建線程繼承了現(xiàn)有的信號(hào)屏蔽字。因?yàn)閟igwait會(huì)解除信號(hào)的阻塞狀態(tài),所以只有一個(gè)線程可以用于信號(hào)的接收。這使得對(duì)主線程進(jìn)行編碼時(shí)不必?fù)?dān)心來(lái)自這些信號(hào)的中斷。
運(yùn)行這個(gè)程序可以得到與程序10-16類似的輸出結(jié)果:
interrupt 鍵入中斷字符interrupt 再次鍵入中斷字符interrupt 再一次 用結(jié)束字符終止
本篇博文內(nèi)容摘自《UNIX環(huán)境高級(jí)編程》(第二版),僅作個(gè)人學(xué)習(xí)記錄所用。關(guān)于本書可參考:http://www.apuebook.com/。
新聞熱點(diǎn)
疑難解答
圖片精選