2011-11-17 63 views
2

我正在研究使用sigprocmask來阻止特定信號(在這種情況下,SIGALRMSIGCHLD),當執行一段關鍵的代碼段時。與這些信號相關聯的兩個信號處理程序都將訪問和修改中央數據結構,所以當主進程正在處理它時,阻止它們訪問它是至關重要的。信號執行期間sigprocmask

目前,我的計劃是在代碼的關鍵部分開始時簡單地禁用這些信號,然後在最後重新啓用它們。

void criticalFunction(void) { 
    // disable signals with sigprocmask 
    // critical code 
    // enable signals with sigprocmask 
} 

但是,信號處理程序對於將被阻止的信號也會呼叫criticalFunction。當他們調用sigprocmask函數並啓用自己的信號阻塞時會發生什麼?他們會失速還是繼續執行? (或第三個條件。)

唯一要注意的我能夠找到的有關這個如下:

如果sigprocmask()執行被稱爲一個信號處理程序,從 處理程序返回可以撤銷sigprocmask()通過恢復原始的 未決信號掩碼的工作。 (http://www.mkssoftware.com/docs/man3/sigprocmask.3.asp

(這是一個後續問題我剛纔的問題:Signal handler accessing queue data structure (race condition?)

回答

2

你的設計是錯誤的,當你說 「信號處理[...]叫criticalFunction。」信號處理程序不應該做任何大量的工作。他們不是爲此而製造的。

您應該從信號處理程序中合理地做的唯一事情是修改類型爲sigatomic_t的變量。這通常用於設置一個標誌,並且代碼的其餘部分(例如主循環)只需定期檢查是否設置了任何標誌。

我認爲它實際上是未定義的行爲如果一個信號處理程序做了除此之外的任何事情。 更新:man 2 signal:「請參閱signal(7)瞭解可從信號處理程序內安全調用的異步信號安全函數列表。」

+0

POSIX定義了在信號處理程序中執行某些操作的行爲,並且您可以執行相當多的操作。這只是非POSIX,普通的C環境,你無法在信號處理程序中做任何事情,並且在這樣的環境中,無論如何都沒有有用的信號,對於信號處理程序來說沒有用處...... –

+0

@ R:是的確,所有的「異步信號安全」功能都可以。特別是,您可以更改信號處理程序內的信號掩碼和其他與信號有關的東西。但就一般用戶代碼而言,最好不要使用信號處理程序。否則,你必須證明你所做的每一件事情都是異步信號安全的,而且這可能是非本地的,很難做到。 –

+0

如果您知道異步信號不安全的功能未被信號處理程序中斷,您可以自由地從信號處理程序調用**任何**功能。如果您仔細地屏蔽並揭露信號,即使在多線程代碼中,也很容易確保這一點。 –

4

請記住,信號處理程序內部的默認行爲是阻止正在處理的信號。在信號處理程序內部進行函數調用時,只需調用信號安全函數。這就是說,sigprocmask()是一個signal-safe function,如果你使用它來阻止信號處理程序阻塞的信號被內部調用,那麼真的什麼都不會發生......你將繼續與您目前擁有的信號掩碼相同。唯一的區別是,在信號處理器內部,只有SIGALRMSIGCHLD的信號被保證被阻塞(這取決於您所在的信號處理程序),其中 - 當您撥打sigprocmask()來阻止這些特定信號時,這兩個信號將在通話後被阻止。

需要注意的事情是在criticalFunction代碼的第二部分,當你試圖調用sigprocmask()使當前擋在信號屏蔽的信號。這可能會造成一種情況,最終會導致對信號處理程序的調用重新進入。換句話說,爲信號處理程序啓用信號可能意味着在退出當前信號處理程序之前,會捕獲另一個SIGALRMSIGCHLD,並且您將再次重新輸入信號處理程序以處理新的問題抓到信號。只要你在任何關鍵部分更新後啓用信號,那麼我認爲你應該適應這種重入狀況,但只是爲了安全起見,你可能只想啓用criticalFunction中的信號criticalFunction的末尾,而不是在中間的某個位置,並且當您從criticalFunction返回時,請不要做任何不會異步安全的操作......您必須假定第二個sigprocmask()返回後的任何代碼可能不是按順序執行(即它可能在第二個信號被捕獲並且其信號處理程序運行後執行)。

如果您試圖從exec家族中調用某些東西,或者您的信號處理程序中存在某種性質的東西,那麼您只需要關心「失速」。會發生什麼情況是新覆蓋的進程會繼承當前進程的信號掩碼,因此如果當前進程阻塞了某些信號,那麼它們也將在新進程中被阻塞。因此,如果新進程假定信號被解除阻塞,那麼新進程中的信號處理程序將永遠不會運行。

順便說一句,一個警告:不要混合信號和線程!你在你的問題中提到「主要過程」......我希望這並不意味着你正試圖混合信號和線程。如果是這樣,那就需要一個非常具體的習慣用語,否則你會造成各種破壞。

+2

通過使用'sigprocmask()'恢復'criticalFunction()'末尾的前一個信號掩碼,而不是盲目解除你關心的信號(即在'criticalFunction ()'''''''''''''''''''''''''sigprocmask(SIG_BLOCK,&critical_sigs,&old_sigs);'最後你會做'sigprocmask(SIG_SETMASK,&old_sigs,NULL);'。 – caf

+0

我沒有在這個系統中使用線程 - 信號是爲了避免使用線程對不起,選擇不好的字 – BSchlinker

+0

@Jason,從信號處理程序修改無鎖數據結構是否安全?sigprocmask()被認爲是解決我的問題的可能方法在我以前的(鏈接)的問題。但是,從你的鏈接看起來像修改一個隊列仍然可能導致腐敗。我想我可以使用一個無鎖隊列,而不是確定是否放置所有問題。 – BSchlinker