[英]Unix pthreads and signals: per thread signal handlers
我無法讓線程捕獲正確的信號。
例如,
我首先開始一個主線程(tid 1)。
然后,它使用signal(2)
SIGUSR1
的信號處理程序設置為function1( signal(2)
。
主線程使用tid 2創建一個新線程。
在線程2中,我使用signal(2)
SIGUSR1
的信號處理程序注冊到function2()
signal(2)
。
然后,線程1創建一個線程3(tid 3)。
從線程3,我使用pthread_kill(1, SIGUSR1)
向線程1發送信號。
但是, function2()
被調用,而不是function1()
。
是出於這種行為,還是需要更改以使這些信號處理程序工作?
編輯:我已經完成了一些調試,結果是信號IS被發送到線程1,但是function2()
由於某種原因從線程1調用。 這有解決方法嗎?
除了alk的答案 :
您可以使用每線程函數指針以每線程方式選擇在傳遞特定信號時執行的函數。
注意:信號將傳遞給未明確阻止其傳遞的任何線程。 這並沒有改變這一點。 您仍然需要使用pthread_kill()
或類似機制將信號定向到特定線程; 引發或發送到進程(而不是特定線程)的信號仍將由隨機線程(在那些不阻塞它的線程中)處理。
我想不出任何個人喜歡這種方法的用例; 到目前為止,總會有其他方式,其他方式更容易和更好。 因此,如果您正在考慮為實際應用程序實現類似的功能,請退一步並重新考慮您的應用程序邏輯。
但是,由於該技術是可行的 ,這里我可以實現它:
#include <signal.h>
/* Per-thread signal handler function pointer.
* Always use set_thread_SIG_handler() to change this.
*/
static __thread void (*thread_SIG_handler)(int, siginfo_t *, void *) = (void *)0;
/* Process-wide signal handler.
*/
static void process_SIG_handler(int signum, siginfo_t *info, void *context)
{
void (*func)(int, siginfo_t *, void *);
#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7)
func = __atomic_load_n(&thread_SIG_handler, __ATOMIC_SEQ_CST);
#else
func = __sync_fetch_and_add(&thread_SIG_handler, (void *)0);
#endif
if (func)
func(signum, info, context);
}
/* Helper function to set new per-thread signal handler
*/
static void set_thread_SIG_handler(void (*func)(int, siginfo_t *, void *))
{
#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 7)
__atomic_store_n(&thread_SIG_handler, func, __ATOMIC_SEQ_CST);
#else
void (*oldfunc)(int, siginfo_t *, void *);
do {
oldfunc = thread_SIG_handler;
} while (!__sync_bool_compare_and_swap(&thread_SIG_handler, oldfunc, func));
#endif
}
/* Install the process-wide signal handler.
*/
int install_SIG_handlers(const int signum)
{
struct sigaction act;
sigemptyset(&act.sa_mask);
act.sa_sigaction = process_SIG_handler;
act.sa_flags = SA_SIGACTION;
if (sigaction(signum, &act, NULL))
return errno;
return 0;
}
我喜歡上面因為它不需要pthreads,並且非常強大和可靠。 除了由於使用預處理器邏輯選擇使用哪種樣式的原子內置函數而造成的視覺混亂之外,如果仔細觀察它,它也非常簡單。
GCC 4.7及更高版本提供類似C ++ 11的__atomic內置函數 ,較舊的GCC版本和其他編譯器(ICC,Pathscale,Portland Group)提供__sync遺留內置函數 。 線程局部存儲的__thread
關鍵字應該類似地在所有當前POSIX-y系統中可用。
如果您有一個過時的系統,或堅持遵守標准,則以下代碼應具有大致相同的行為:
#include <pthread.h>
#include <signal.h>
#include <errno.h>
static pthread_key_t thread_SIG_handler_key;
static void process_SIG_handler(int signum, siginfo_t *info, void *context)
{
void (*func)(int, siginfo_t *, void *);
*((void **)&func) = pthread_getspecific(thread_SIG_handler_key);
if (func)
func(signum, info, context);
}
static int set_thread_SIG_handler(void (*func)(int, siginfo_t *, void *))
{
sigset_t block, old;
int result;
sigemptyset(&block);
sigaddset(&block, SIG); /* Use signal number instead of SIG! */
result = pthread_sigmask(SIG_BLOCK, &block, &old);
if (result)
return errno = result;
result = pthread_setspecific(thread_SIG_handler_key, (void *)func);
if (result) {
pthread_sigmask(SIG_SETMASK, &old, NULL);
return errno = result;
}
result = pthread_sigmask(SIG_SETMASK, &old, NULL);
if (result)
return errno = result;
return 0;
}
int install_SIG_handlers(const int signum)
{
struct sigaction act;
int result;
result = pthread_key_create(&thread_SIG_handler_key, NULL);
if (result)
return errno = result;
sigemptyset(&act.sa_mask);
act.sa_sigaction = process_SIG_handler;
act.sa_flags = SA_SIGACTION;
if (sigaction(signum, &act, NULL))
return errno;
return 0;
}
我認為與我實際使用過的最接近現實代碼的代碼是我使用一個實時信號( SIGRTMIN+0
)在除了一個線程之外的所有線程中被阻擋的一個,作為反射器:它發送了另一個實時信號( SIGRTMIN+1
)到多個工作線程,以便中斷阻塞I / O. (可以使用單個實時信號執行此操作,但雙信號模型實現起來更簡單,更易於維護。)
這種信號反射或扇出有時是有用的,並且與這種方法沒有什么不同。 但是,如果有人感興趣的話,不同的是保證自己的問題。
無法安裝“每線程”信號處理程序。
從man 7 signal
(由我強調):
信號處理是每進程屬性:在多線程應用程序中,特定信號的處置對於所有線程都是相同的。
然而, 可以以引導每一信號類型到一個不同的線程,通過在“每線程”基地屏蔽掉任何數量的信號類型的接收。
關於如何將一組信號類型指向特定的線程,您可能希望看看這個答案: https : //stackoverflow.com/a/20728819/694576
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.