2009-11-01 51 views
2

第10.6節中的示例代碼,預期的結果是:
經過多次迭代後,getpwnam使用的靜態結構將被破壞,並且程序將以SIGSEGV信號。即使在處理程序中重置,SIGALRM的信號處理程序也不起作用

但我的平臺的Fedora 11,GCC上(GCC)4.4.0,其結果是

[Langzi @自由APUE] $ ./corrupt
在sig_alarm

我只能看到sig_alarm的輸出一次,程序似乎因爲某種原因掛起,但它確實存在,並且仍在運行。
但是,當我嘗試使用gdb來運行程序,似乎沒問題,我會定期看到從sig_alarm的輸出。

從我的手冊中,它說信號處理程序將在信號處理後設置爲SIG_DEF,並且系統不會阻止信號。所以在我的信號處理程序開始時,我重置了信號處理程序。

也許我應該使用sigaction來代替,但我只想知道正常運行和gdb運行之間區別的原因。

任何意見和幫助將不勝感激。

下面是我的代碼:

#include "apue.h" 
#include <pwd.h> 

void sig_alarm(int signo); 

int main() 
{ 
    struct passwd *pwdptr; 
    signal(SIGALRM, sig_alarm); 

    alarm(1); 
    for(;;) { 
    if ((pwdptr = getpwnam("Zhijin")) == NULL) 
     err_sys("getpwnam error"); 
    if (strcmp("Zhijin", pwdptr->pw_name) != 0) { 
     printf("data corrupted, pw_name: %s\n", pwdptr->pw_name); 
    } 
    } 
} 

void sig_alarm(int signo) 
{ 
    signal(SIGALRM, sig_alarm); 
    struct passwd *rootptr; 
    printf("in sig_alarm\n"); 

    if ((rootptr = getpwnam("root")) == NULL) 
    err_sys("getpwnam error"); 
    alarm(1); 
} 

回答

7

根據標準,你真的不能做太多的信號處理程序。您保證能夠在信號處理功能中執行所有操作,而不會導致不確定的行爲,請致電信號,並將值分配給sig_atomic_t類型的易失性靜態對象。

我在Ubuntu Linux上運行這個程序的前幾次,看起來像您在報警在信號處理程序中沒有工作,所以主循環在第一次報警後仍然運行。當我稍後嘗試時,程序運行了幾次信號處理程序,然後掛起。所有這些與未定義的行爲是一致的:程序有時會失敗,並且或多或少地有趣。

對於具有未定義行爲的程序來說,在調試器中以不同的方式工作並不罕見。調試器是一個不同的環境,你的程序和數據可以用不同的方式在內存中進行佈局,所以錯誤可以以不同的方式表現出來,或者根本不表現出來。

我得到的程序中加入一個變量來工作:

volatile sig_atomic_t got_interrupt = 0; 

然後,我改變了你的信號處理這個非常簡單的一個:

void sig_alarm(int signo) { 
    got_interrupt = 1; 
} 

然後我插入的實際工作納入在主要的無限循環:

if (got_interrupt) { 
    got_interrupt = 0; 
    signal(SIGALRM, sig_alarm); 
    struct passwd *rootptr; 
    printf("in sig_alarm\n"); 

    if ((rootptr = getpwnam("root")) == NULL) 
     perror("getpwnam error"); 
    alarm(1); 
} 

我認爲你提到的「apue」是書「高級在UNIX環境下編程「,這裏我沒有,所以我不知道這個例子的目的是爲了表明你不應該混淆信號處理器內部的東西,或者只是說信號可以通過中斷程序的正常工作而導致問題。

+0

感謝您的回覆。該示例的目的是爲了顯示我們是否從信號處理程序調用不可重入函數,結果是不可預測的。 Nonreentrant函數:a)使用靜態數據結構,b)調用malloc或free,c)使用標準I/O庫,因爲該庫以非新生方式使用全局數據結構。我們應該保存並恢復errno。 函數getpwnam使用靜態結構,因此在主循環中,調用可能會發現內部數據指針在信號處理程序調用相同函數時已損壞,並且程序將崩潰。 和 – OnTheEasiestWay 2009-11-01 11:09:19

+0

並按照你的建議,呼叫警報是好的。但是該示例的預期結果不能顯示。這個例子是爲了演示關於信號處理器的錯誤方式,所以我只想知道它出錯的原因。再次感謝。 – OnTheEasiestWay 2009-11-01 11:20:22

+0

是的,這個例子顯示你得到未定義的行爲。我假定其意圖是,字符串** pw_name **有時應該是錯誤的。但在我看來,它實際上已經顯示了更多,即未定義的行爲就是這樣,沒有定義,如果你期望它做一件特定的事情,它可能會做一些完全不同的事情。只是很高興它沒有融化電腦,或發送粗魯的電子郵件給你的母親! – 2009-11-02 10:00:34

1

根據規範,函數getpwnam不可重入,並且不保證是線程安全的。由於您在兩個不同的控制線程中訪問結構(信號處理程序在不同的線程環境中有效運行),因此您正在遇到此問題。每當你有併發或並行執行時(就像使用pthreads或使用信號處理程序時一樣),你必須確保不要修改共享狀態(例如'getpwnam'擁有的結構),如果你這樣做,那麼適當的鎖定/同步必須使用。

此外,signal函數已被棄用,以支持sigaction函數。爲了確保註冊信號處理程序時的便攜行爲,您應始終使用sigaction調用。

使用sigaction函數,可以使用SA_RESETHAND標誌來重置默認處理程序。您還可以使用sigprocmask函數來啓用/禁用信號傳遞,而無需修改其處理程序。

1
#include <stdio.h> 
#include <stdlib.h> 
#include <signal.h> 
#include <unistd.h> 

void sigalrm_handler(int); 




int main() 
{ 
    signal(SIGALRM, sigalrm_handler); 

    alarm(3); 


    while(1) 
    { 

    } 

    return 0; 
} 


void sigalrm_handler(int sign) 
{ 

    printf("I am alive. Catch the sigalrm %d!\n",sign); 
    alarm(3); 
} 

例如,我的代碼是在主無所事事乳寧,並每3秒我的計劃說,即時通訊活躍X)

我認爲,如果你這樣做,因爲我做了與價值的處理函數報警主叫3,問題解決了:)