2010-11-21 25 views
5

如果一個程序調用一個函數,該函數是一個來自信號處理程序的取消點,會發生什麼情況?有許多POSIX指定爲異步信號安全和取消點的功能。如果一個信號處理程序調用這樣一個函數並且執行了取消操作,那麼結果與如果線程啓用了異步取消會發生的情況非常相似 - 實際上更糟糕的是,因爲所有取消清除處理程序(可能不是異步信號)安全,將從信號處理程序環境中調用。信號處理程序中的取消點?

在這種情況下,POSIX實際指定了什麼?實現實際上做了什麼?我無法在POSIX中找到任何禁止信號處理程序中的取消點被執行的語言,也沒有在glibc/nptl源中提供任何此類保護。

回答

2

我不知道POSIX甚至敢提這個話題,但我沒有做過詳盡的搜索。

對gcc/nptl系統進行的一些簡短的實驗表明,正如我懷疑的那樣,我認爲你也這樣做了,NPTL中沒有這樣的保護 - 取消處理程序確實從信號處理程序的上下文中調用。

下面的程序(道歉爲hackiness等)將顯示以下輸出:

Signal handler called 
Sent cancellation 
Cleanup called 
In sighandler 

...指示:

  • 信號處理程序得到了稱爲
  • 其他線程然後稱爲pthread_cancel()
  • 取消處理程序然後被調用,沒有信號處理程序完成

這裏的程序:

#include <stdio.h> 
#include <pthread.h> 
#include <signal.h> 
#include <string.h> 
#include <unistd.h> 
#include <assert.h> 

pthread_t mainthread; 

int in_sighandler = 0; 

void 
cleanup (void *arg) 
{ 
    write(1, "Cleanup called\n", strlen("Cleanup called\n")); 
    if (in_sighandler) { 
     write(1, "In sighandler\n", strlen("In sighandler\n")); 
    } else { 
     write(1, "Not in sighandler\n", strlen("In sighandler\n")); 
    } 
} 


void 
sighandler (int sig, siginfo_t *siginfo, void *arg) 
{ 
    in_sighandler = 1; 
    write(1,"Signal handler called\n", strlen("Signal handler called\n")); // write() is a CP 
    usleep(3000000); // usleep() is a CP; not strictly async-signal-safe but happens to be so in Linux 
    write(1, "Signal handler exit\n", strlen("Signal handler exit\n")); 
    in_sighandler = 0; 
} 

void * 
thread (void *arg) 
{ 
    sleep(1); 
    pthread_kill(mainthread, SIGUSR1); 
    usleep(500000); 
    pthread_cancel(mainthread); 
    printf("Sent cancellation\n"); 
    return (NULL); 
} 

int 
main (int argc, char **argv) 
{ 
    int rc; 
    struct sigaction sa; 
    pthread_t threadid; 

    mainthread = pthread_self(); 

    // Set up a signal handler to test its cancellation properties 
    sa.sa_sigaction = &sighandler; 
    sigemptyset(&sa.sa_mask); 
    sa.sa_flags = SA_SIGINFO; 
    rc = sigaction(SIGUSR1, &sa, NULL); 
    assert(rc == 0); 

    // Set up a thread to send us signals and cancel us 
    rc = pthread_create(&threadid, NULL, &thread, NULL); 
    assert(rc == 0); 

    // Set up cleanup handlers and loop forever 
    pthread_cleanup_push(&cleanup, NULL); 
    while (1) { 
     sleep(60); 
    } 
    pthread_cleanup_pop(0); 
    return (0); 
} 
+1

短的任何更好的答案就進來,我可能會接受你的。據我所知,這基本上只意味着你不能在程序中使用取消,除非你的信號處理程序本身禁止取消或避免調用任何可能是取消點的功能。不幸的是,這意味着利用沒有調用程序的意識/合作的線程的庫代碼根本不能使用取消(因爲調用程序可能具有設置信號處理程序)。任何使用都可能導致信號處理程序被取消的競態條件。 – 2010-11-23 22:45:52

+2

是的,沒錯 - 雖然嚴格來說信號處理程序不能禁用取消(pthread函數不是異步信號安全的)。該庫*可以*阻止*創建任何線程中的所有*信號,但這並不理想(尤其是因爲在Linux上阻止SIGRTMIN會禁用取消...)。我一直認爲取消是很危險的,除非你確定這個庫的設計是以取消爲目的的,否則你不能從一個可取消的線程調用任何庫函數(否則它可能會分配資源,調用取消點函數,釋放這些資源......) – psmears 2010-11-24 10:39:10

+0

爲什麼它的價值我剛剛在運行Solaris 10的計算機上嘗試過相同的程序,結果相同......我想這意味着,即使它確實發生了變化標準使得這個安全,它是沒用的,因爲最常見的實現不支持它: -/ – psmears 2010-11-24 10:51:46