2011-09-09 24 views
2

我用下面的例子工作(這是基於聯機幫助頁在linux pthread_sigmask的例子):信號用C處理與流程和2個線程不工作

#include <pthread.h> 
#include <stdio.h> 
#include <stdlib.h> 
#include <unistd.h> 
#include <signal.h> 
#include <errno.h> 
#include <string.h> 

/* Simple error handling functions */ 

#define handle_error_en(en, msg) \ 
     do { errno = en; perror(msg); exit(EXIT_FAILURE); } while (0) 

static void * silly_worker(void *arg) 
{ 
    for(int index=0,max=5; index<max; ++index) { 
    printf("waiting %d of %d\n",index,max); 
    sleep(1); 
    } 
    puts("Finished waiting. Here comes the SIGSEGV"); 
    strcpy(NULL,"this will crash"); 
} 

static void * 
sig_thread(void *arg) 
{ 
    sigset_t *set = (sigset_t *) arg; 
    int s, sig; 

    for (;;) { 
     s = sigwait(set, &sig); 
     if (s != 0) 
      handle_error_en(s, "sigwait"); 
     printf("Signal handling thread got signal %d\n", sig); 
    } 
} 

int 
main(int argc, char *argv[]) 
{ 
    pthread_t thread; 
    pthread_t thread2; 
    sigset_t set; 
    int s; 

    /* Block SIGINT; other threads created by main() will inherit 
     a copy of the signal mask. */ 

    sigemptyset(&set); 
    sigaddset(&set, SIGQUIT); 
    sigaddset(&set, SIGUSR1); 
    sigaddset(&set, SIGSEGV); 
    s = pthread_sigmask(SIG_BLOCK, &set, NULL); 
    if (s != 0) 
     handle_error_en(s, "pthread_sigmask"); 

    s = pthread_create(&thread, NULL, &sig_thread, (void *) &set); 
    if (s != 0) 
     handle_error_en(s, "pthread_create"); 

    /* Main thread carries on to create other threads and/or do 
     other work */ 

    s = pthread_create(&thread2, NULL, &silly_worker, (void *) &set); 
    if (s != 0) 
     handle_error_en(s, "pthread_create"); 

    pause();   /* Dummy pause so we can test program */ 
} 

據該男子頁面,這應該捕獲由silly_worker線程生成的SIGSEGV。但事實並非如此。事實上,我不確定哪個機構能夠獲得信號。當程序運行時,我得到下面的輸出:

waiting 0 of 5 
waiting 1 of 5 
waiting 2 of 5 
waiting 3 of 5 
waiting 4 of 5 
Finished waiting. Here comes the SIGSEGV 
Segmentation fault 

你可以看到,信號處理程序不輸出「段錯誤」的字符串,因此它必須從默認的處理器到來。如果是默認值,那麼它會破壞該示例的目的 - 設置信號處理程序並捕獲信號並對它們執行某些操作。

我可以找到很多處理程序的例子,但沒有一個適用於這種情況:它們都沒有演示一個導致真正明顯的SIGSEGV的線程,並捕獲並報告自定義處理程序中的錯誤。

問題依然存在:如何獲取自定義信號處理程序以從此SIGSEGV'ing線程獲取信號?

+0

這是否適用於SIGINT等其他信號?一些像SIGSEGV和SIGILL這樣的信號是同步的,並且總是被傳遞給引起它們的線程。我不確定sigwait是否可以處理這些問題。 –

+0

請注意,「Segmentation fault」(分段錯誤)通常由Shell在看到您的進程被SIGSEGV信號終止時打印出來。 – Neil

+0

它適用於其他信號,但這不會有幫助:我試圖捕獲SIGSEGV具體。在main()和沒有線程的單個循環中,我可以捕獲SIGSEGV,這完全沒有問題。當線程發揮作用時,它似乎並不正確。 –

回答

1

SIGSEGV來自無效的存儲器訪問(而不是由「kill」或「sigqueue」發送的「虛假」的)發送到執行無效存儲器訪問的線程,而不是整個進程。因此你不能有一個專用的段錯誤處理程序線程。如果你想處理它,你必須在它發生的線程中處理它。 (你看到shell打印Segmentation fault的原因是,當SIGSEGV在線程中被阻塞並且發生段錯誤時,內核執行殺死進程的默認操作,實際上它是每個POSIX的UB,但這是Linux處理UB)

但是請注意,您可以讓SIGSEGV的信號處理程序通過專用線程觸發操作。一個醜陋的方式來做到這一點將與另一個信號,你可以通過sigqueue發送(連同一個參數)。更乾淨的方式(至少在我看來)是讓SIGSEGV處理程序使用sem_post,它是異步信號安全的,可用於喚醒另一個等待信號量的線程。

+0

+1。您無法使用SIGSEGV「如果SIGBUS,SIGFPE,SIGILL或SIGSEGV在 被阻止時生成,結果是未定義的,除非信號是由kill(2),sigqueue(2)或raise(3)。 「 –

+0

因此,不同於其他任何信號類型,sigsegv總是被傳遞給Linux上的觸發線程?該信號進入隨機線程通常是在所有其他線程被屏蔽的情況下擁有專用信號線程的原因。 –

+0

通過'kill'或通過終端(例如Ctrl-C)發送到進程的信號轉到任意線程(信號未被屏蔽的線程集合中)。這些是*異步*信號。作爲線程某些動作(如無效內存訪問,寫入管道或讀取端關閉的套接字等)的直接結果而發生的信號是* synchronous *信號並轉到行爲生成它們的線程。 –