2017-09-10 38 views
7

我正在玩C中的信號。我的主要功能基本上要求使用fgets(name, 30, stdin)進行一些輸入,然後坐在那裏等待。我用alarm(3)設置了一個鬧鐘,然後我重新分配了SIGALRM來調用函數myalarm,該函數簡單地調用system("say PAY ATTENTION")。但是在鬧鈴響起後,fgets()停止等待輸入,我的主要fn繼續。即使我將myalarm更改爲只設置了一些變量並且什麼也不做,會發生這種情況。爲什麼alarm()會導致fgets()停止等待?

void myalarm(int sig) { 
    //system("say PAY ATTENTION"); 
    int x = 0; 
} 

int catch_signal(int sig, void (*handler)(int)) { // when a signal comes in, "catch" it and "handle it" in the way you want 
    struct sigaction action; // create a new sigaction 
    action.sa_handler = handler; // set it's sa_handler attribute to the function specified in the header 
    sigemptyset(&action.sa_mask); // "turn all the signals in the sa_mask off?" "set the sa_mask to contian no signals, i.e. nothing is masked?" 
    action.sa_flags = 0; // not sure, looks like we aren't using any of the available flags, whatever they may be 

    return sigaction(sig, &action, NULL); // here is where you actually reassign- now when sig is received, it'll do what action tells it 
} 

int main() { 

    if(catch_signal(SIGINT, diediedie)== -1) { 
     fprintf(stderr, "Can't map the SIGINT handler"); 
     exit(2); 
    } 

    if(catch_signal(SIGALRM, myalarm) == -1) { 
     fprintf(stderr, "Can't map the SIGALAM handler\n"); 
     exit(2); 
    } 

    alarm(3); 

    char name[30]; 
    printf("Enter your name: "); 
    fgets(name, 30, stdin); 

    printf("Hello, %s\n", name); 

    return 0; 

} 

爲什麼alarm()使fgets()停止等待輸入?

編輯:爲我的catch_signal函數添加了代碼,並且根據其中一個註釋,使用sigaction而不是signal,但問題依然存在。

+5

請參閱'man 7 signal',子標題爲「通過信號處理程序中斷系統調用和庫函數」 – Retr0id

+0

我看到這一點:「如果對信號處理程序中斷了以下某個接口的阻塞呼叫,如果使用SA_RESTART標誌,則在信號處理程序返回後調用將自動重新啓動;否則調用將失敗,並顯示錯誤EINTR ...「並且fgets()未被列爲可重新啓動的函數之一。我試圖做的是不可能的? –

+0

'fgets()'在引擎蓋下使用'read'系統調用。順便說一句,在我的系統上它不會中斷(Arch Linux x86_64) – Retr0id

回答

4

答案很可能是OS /系統特定的。 (如Retr0spectrum所述)fgets()函數通常會進行系統調用,例如read()。如果檢測到信號,系統調用可能會終止。在這個問題的情況下,fgets()函數已經進行了系統調用(可能是read()系統調用)從stdin讀取一個字符。 SIGALRM導致系統調用終止,並將errno設置爲EINTR。這也會導致fgets()函數終止,而不讀取任何字符。

這並不罕見。這就是操作系統如何實現信號。

爲了避免這個問題,我會經常換與fgets()函數在這樣的循環:

do { 
    errno=0; 
    fgets(name, 30, stdin); 
    } while(EINTR == errno); 

它會要求你:#include <stdio.h>

(如TonyB建議)。

+0

我會在*「fgets()函數經常使系統調用」* ;-)後添加[需要的引用] – spectras

+0

這裏的循環是錯誤的。 Stdio功能不能像那樣重新啓動;錯誤後未指定緩衝區內容和文件位置。如果您使用的是stdio,並且不希望信號成爲致命錯誤,您必須**將'sigaction'與'SA_RESTART'一起使用。 –

3

至於爲什麼報警信號中斷讀的的問題,有兩個方面的原因:

  1. 它的Unix用來做什麼的方式,這是因爲它更容易實現在OS中。 (一方面這聽起來有些蹩腳,但「不要汗流hard背」的態度對於Unix的成功起到了一定的作用。這是Richard P. Gabriel的史詩主題更糟的是更好的短文。)

  2. 它可以很容易地實現一個讀超時和放棄,如果這就是你想要的。 (見我的回答this other question

但是,正如其他意見和解答討論,中斷的行爲是有點過時;大多數現代系統(至少Unix和Linux)現在會自動重啓一箇中斷的系統調用,如read,或多或少地按照您的意願重啓。 (也正如其他地方指出的那樣,如果你知道自己在做什麼,你可能可以在兩種行爲之間進行選擇。)

最後,雖然是灰色地帶,我非常確定C標準沒有指定它,或者實現定義或未定義如果您使用警報或其他信號中斷系統調用會發生什麼情況。

相關問題