12.8 线程和信号
在基于進(jìn)程模型的信號(hào)處理已經(jīng)比較嗎麻煩了,引入線程后事情就更加復(fù)雜了。
每個(gè)線程擁有其自身的信號(hào)掩碼,但是信號(hào)處理函數(shù)是被進(jìn)程內(nèi)的所有線程共享的,作為一個(gè)推論,獨(dú)立的線程能夠阻塞信號(hào),但是如果一個(gè)線程修改與給定信號(hào)的處理動(dòng)作的時(shí)候,所有的線程都會(huì)共享這一修改。也就是說,如果一個(gè)線程選擇忽略一個(gè)給定信號(hào),其他的線程可能會(huì)通過恢復(fù)默認(rèn)呢處理或者是安裝信號(hào)處理函數(shù)的方式撤銷線程所做的忽略選擇。
信號(hào)會(huì)只會(huì)被發(fā)送到進(jìn)程內(nèi)的一個(gè)線程。如果信號(hào)與硬件錯(cuò)誤相關(guān),那么信號(hào)通常被發(fā)送到造成時(shí)間發(fā)生的線程,其他信號(hào),將被發(fā)送到任意一個(gè)線程。
在10.12節(jié)中,我們介紹了進(jìn)程可以使用函數(shù)sigprocmask函數(shù)來阻塞信號(hào)的發(fā)送,然而,sigprocmask函數(shù)在多線程進(jìn)程中的行為是未定義的,線程必須使用函數(shù)pthread_sigmask予以代替。
函數(shù)pthread_sigmask與函數(shù)sigprocmask相似,但是pthread_sigmask函數(shù)在失敗的時(shí)候返回一個(gè)錯(cuò)誤碼,而函數(shù)sigprocmask在失敗的時(shí)候返回-1并且設(shè)置errno變量。
參數(shù)how可以取三個(gè)不同的數(shù)值:
* SIG_BLOCK添加信號(hào)集到線程的信號(hào)掩碼中;
* SIG_SETMASK使用信號(hào)集替換線程的信號(hào)掩碼;
* SIG_UNBLOCK從線程的信號(hào)掩碼中移除信號(hào)集;
如果oset參數(shù)不是null,那么線程的前一個(gè)信號(hào)掩碼將被存儲(chǔ)到oset指針指向的內(nèi)存中。線程可以通過設(shè)置set參數(shù)為null的情況下獲取其當(dāng)前的信號(hào)掩碼,著這種情況下,how參數(shù)將被忽略。
線程可以調(diào)用函數(shù)sigwait來等待一個(gè)或者多個(gè)信號(hào)的出現(xiàn);
參數(shù)set指定了線程正在等待信號(hào)的集合,參數(shù)signop指針指向的內(nèi)存將會(huì)存儲(chǔ)被發(fā)送的信號(hào)數(shù)量。
如果在sigwait函數(shù)被調(diào)用的時(shí)候,如果set中的一個(gè)信號(hào)處于掛起狀態(tài),那么函數(shù)sigwait就不會(huì)阻塞,而是直接返回,在返回之前,sigwait函數(shù)將會(huì)將從進(jìn)程掛起的信號(hào)集中移除等到的信號(hào)。如果實(shí)現(xiàn)支持信號(hào)排隊(duì),并且一個(gè)信號(hào)的多個(gè)實(shí)例處于掛起狀態(tài),函數(shù)sigwait僅僅移除其中一個(gè)實(shí)例,其他實(shí)例仍然處于排隊(duì)狀態(tài)。
為了避免錯(cuò)誤的行為,線程在調(diào)用函數(shù)sigwait之前必須先阻塞這些等待的信號(hào)。函數(shù)sigwait將會(huì)自動(dòng)解除對(duì)應(yīng)信號(hào)的阻塞,并且一直等待知道其中一個(gè)信號(hào)被發(fā)送,在返回之前,sigwait將會(huì)恢復(fù)線程的信號(hào)掩碼,如果函數(shù)sigwait被調(diào)用的時(shí)候,對(duì)應(yīng)的信號(hào)沒有被阻塞,那么在完成函數(shù)sigwait之前會(huì)出現(xiàn)一個(gè)多于的時(shí)間窗口,在這個(gè)時(shí)間窗口內(nèi),信號(hào)可能被發(fā)送到了線程。
使用函數(shù)sigwait的一個(gè)好處就是可以簡(jiǎn)化信號(hào)處理函數(shù)的設(shè)計(jì),因?yàn)樗试S我們將異步信號(hào)處理當(dāng)作是同步信號(hào)來看待,我們可以通過將指定信號(hào)添加到線程的信號(hào)掩碼中,從而防止信號(hào)中斷線程的執(zhí)行,然后專門指定一個(gè)線程來處理信號(hào),這些指定的線程在調(diào)用函數(shù)的時(shí)候不需要再考慮那些函數(shù)是信號(hào)處理安全的,因?yàn)樵谛盘?hào)處理函數(shù)被調(diào)用的時(shí)候,線程處于正常的線程環(huán)境,而不是像傳統(tǒng)情況那樣:信號(hào)處理函數(shù)能夠中斷線程的正常執(zhí)行。
如果多個(gè)線程由于調(diào)用了函數(shù)sigwait而阻塞在了同一個(gè)信號(hào)上,那么當(dāng)信號(hào)被發(fā)送的時(shí)候,僅僅只有一個(gè)線程將會(huì)返回,如果一個(gè)信號(hào)被捕獲,并且存在一個(gè)線程調(diào)用函數(shù)sigwait等待這一信號(hào),如何發(fā)送信號(hào)是交給實(shí)現(xiàn)了決定的,實(shí)現(xiàn)可以允許sigwait返回或者是調(diào)用信號(hào)處理函數(shù),但是不能同時(shí)執(zhí)行這兩個(gè)動(dòng)作。
為了發(fā)送一個(gè)信號(hào)給進(jìn)程,我們可以調(diào)用函數(shù)kill,為了發(fā)送一個(gè)信號(hào)給一個(gè)線程,我們可以調(diào)用函數(shù)pthread_kill;
我們可以傳遞給參數(shù)signo數(shù)值0,用于檢測(cè)指定線程是否存在。如果一個(gè)信號(hào)的默認(rèn)處理方式是終止進(jìn)程,那么向一個(gè)線程發(fā)送這個(gè)信號(hào)也會(huì)終止掉整個(gè)進(jìn)程。
注意alarm timers是進(jìn)程資源,所有的線程都會(huì)共享相同的alarms集合,因此,對(duì)于一個(gè)進(jìn)程中的多個(gè)線程,不可能做到使用alarm timers而不發(fā)生沖突。
Example
在圖10.23中,我們等待信號(hào)處理函數(shù)設(shè)置一個(gè)標(biāo)志,用于表示主程序可以退出了。在這個(gè)程序中,可以運(yùn)行的程序流僅僅只有主線程以及信號(hào)處理函數(shù),所以阻塞信號(hào)對(duì)于避免丟失標(biāo)志的改變是有效的。使用多線程以后,我們需要使用互斥鎖來保護(hù)標(biāo)志,正如在圖12.16中程序所示。
圖12.16 同步信號(hào)處理
信號(hào)處理函數(shù)不再會(huì)中斷主線程,我們使用一個(gè)專門的線程來處理信號(hào),我們可以在互斥鎖的保護(hù)下修改quitflag,so that the main thread of control can’t miss the wake-up call made when we call pthread_cond_signal.我們?cè)谥骶€程中使用同一互斥鎖來檢查標(biāo)記的值,并在我們等待條件的時(shí)候自動(dòng)釋放鎖。
注意同時(shí)阻塞了信號(hào)SIGINT以及SIGQUIT信號(hào),當(dāng)我們創(chuàng)建線程來處理信號(hào)的時(shí)候,線程繼承了當(dāng)前的信號(hào)掩碼,因?yàn)閟igwait并不會(huì)阻塞信號(hào),因此只有一個(gè)線程可以接受信號(hào),這樣可以使得我們?cè)诰帉懼骶€程的時(shí)候不用考慮這些中斷信號(hào)的干擾。
程序運(yùn)行情況如下入所示:
來自為知筆記(Wiz)
轉(zhuǎn)載于:https://www.cnblogs.com/U201013687/p/5635947.html
總結(jié)
以上是生活随笔為你收集整理的12.8 线程和信号的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。