2010-04-04 141 views
72

我一直在試圖瞭解如何POSIX線程和POSIX信號相互作用的複雜性。特別是,我感興趣的是:POSIX線程和信號

  • 什麼是控制信號傳遞到哪個線程的最佳方式(假設它首先不是致命的)?
  • 什麼是告訴另一個線程(這實際上可能是忙),該信號已經來臨的最好方法? (我已經知道,這是一個壞主意,利用從信號處理並行線程條件變量。)
  • 我怎樣才能安全地處理傳遞一個信號發生了其他線程的信息?這是否需要在信號處理程序中發生? (我做的不是一般的想殺死其他線程。我需要一個遠微妙的方式)

僅供參考我爲什麼想要這個,我研究如何將TclX包,支持線程轉換,或分解它,並至少使一些有用的部分支持線程。信號是特別感興趣的部分之一。

回答

42
  • 什麼是控制哪個線程 的信號傳遞到最好的方法是什麼?

正如@ zoli2k指出,明確提名單個線程來處理您要處理的所有信號(或一組每個特定的信號職責線程),是一個很好的技術。

  • 什麼是告訴另一個線程(這實際上可能是忙) 該信號已經到達的最佳方式?[...]
  • 我怎樣才能安全地處理傳遞信息的信號其他線程發生了 ?這是否需要在信號處理程序中發生?

我不會說「最好」,但這裏是我的建議:

阻止所有在main所需的信號,讓所有線程都繼承該信號屏蔽。然後,將特殊信號接收線程作爲信號驅動事件循環,將新到達的信號調度爲其他一些線程內通信

最簡單的方法是讓線程接受使用sigwaitinfo or sigtimedwait的循環中的信號。然後線程以某種方式轉換信號,或許廣播一個pthread_cond_t,喚醒具有更多I/O的其他線程,將命令排入特定於應用程序的線程安全隊列中,無論如何。

或者,特殊線程可以允許將信號傳遞給信號處理程序,僅在準備好處理信號時才屏蔽傳遞。然而,在這種情況下,接收器的信號處理器執行一些簡單且異步信號安全的操作:設置sig_atomic_t標誌,調用sigaddset(&signals_i_have_seen_recently, latest_sig),write(通過處理器發送信號往往比通過sigwait系列的信號接受更容易出錯)。 ()一個字節到一個非阻塞的self-pipe等。然後,返回到它的被屏蔽的主循環中,線程將信號的接收傳送給其他線程,如上所述。

修訂 @caf正確地指出,sigwait方法是優越的。)

+0

這是一個非常有用的答案,特別是它可以用於處理非致命的信號處理。謝謝! – 2010-04-23 20:49:06

+1

如果信號處理線程根本沒有安裝信號處理程序,那麼它最簡單 - 相反,它在'sigwaitinfo()'(或'sigtimedwait()')上循環,然後按照描述將其分發到應用程序的其餘部分在最後一段。 – caf 2015-07-20 06:55:37

+0

@caf,的確如此。更新 – pilcrow 2015-07-20 16:37:52

13

根據POSIX標準的所有主題應與系統上的相同PID出現並使用pthread_sigmask()您可以定義每個線程阻塞屏蔽信號。

由於每個PID只允許定義一個信號處理程序,因此如果需要取消正在運行的線程,我寧願處理一個線程中的所有信號,併發送pthread_cancel()。這是針對pthread_kill()的首選方式,因爲它允許爲線程定​​義清理函數。

在一些舊系統中,由於缺乏適當的內核支持,正在運行的線程也可以有父線程的PID不同的PID。請參見使用linuxThreads on Linux 2.4進行信號處理的常見問題解答。

+0

在你說的「實施」是什麼意思?另外,總是在響應一個信號時覈對其他線程是不正確的(SIGHUP和SIGWINCH需要更精細),但使用條件變量讓其他線程知道是不安全的。回答不好。 – 2010-04-05 06:09:11

+0

@Donal Fellows>張貼編輯。我希望現在更有幫助。 – zoli2k 2010-04-05 07:32:45

+1

刪除了我的倒票,但它仍然不是一個足夠的答案,因爲我不能只是爲了響應信號而終止線程。在某些情況下,我會在本地排隊處理事件,在其他情況下,我必須非常小心地拆卸線程(順便說一句,我已經有大部分機器來完成這些部分;這是與OS的連接信號丟失)。 – 2010-04-05 07:53:52

3

我在哪裏至今:

  • 信號有不同的主要類別,其中的某些通常只是殺死進程反正(SIGILL)和其中一些從未有什麼需要做的(SIGIO;容易無論如何只是做異步IO)。這兩類不需要採取任何行動。
  • 有些信號不需要立即處理;像SIGWINCH一樣可以排隊等待,直到方便(就像來自X11的事件一樣)。
  • 棘手的有要通過中斷自己在做什麼,但沒有去消滅一個線程的程度做出迴應的人。特別是交互模式下的SIGINT應該讓事情變得敏感。

我還有需要整理signal VS sigactionpselectsigwaitsigaltstack,和一大堆其他位和POSIX(和非POSIX)API的作品。

4

IMHO,Unix的V信號和POSIX線程不拌勻。 的Unix V是1970年POSIX是1980;)

有取消點,如果你允許信號和並行線程在一個應用程序,你會最終結束了寫身邊的每個呼叫,可令人驚訝的返回EINTR循環。

因此,我在(少數)需要在Linux或QNX上編程多線程的情況下所做的工作就是屏蔽所有(但一個)線程的所有信號。

當Unix V Signal到達時,進程切換堆棧(在Unix V中的併發性與在進程中得到的一樣多)。

正如其他帖子在這裏提示,現在可能有可能告訴系統,哪個posix線程應該是堆棧切換的受害者。

有一次,你設法讓你的信號處理線程工作,問題仍然存在,如何將信號信息轉換成文明的,其他線程可以使用的。用於線程間通信的基礎結構是必需的。一種模式,有用的是演員模式,其中每個線程都是某個進程內消息傳遞機制的目標。因此,不要取消其他線程或殺死它們(或其他奇怪的東西),您應該嘗試將Signal從Signal上下文組織到Signal處理程序線程,然後使用您的actor模式通信機制發送語義上有用的消息對那些需要信號相關信息的演員。