2013-10-02 22 views
3

我有一個信號處理程序中設置使用的sigaction像這樣:奇怪的sigaction()和函數getline()的相互作用

struct sigaction act, oldact; 
memset(&act, 0, sizeof(struct sigaction)); 
act.sa_handler = sig_handler; 
sigemptyset(&act.sa_mask); 
sigaddset(&act.sa_mask, SIGALRM); 
sigaddset(&act.sa_mask, SIGINT); 
sigaddset(&act.sa_mask, SIGTERM); 
sigaddset(&act.sa_mask, SIGTSTP); 
sigaction(SIGALRM, &act, &oldact); 
sigaction(SIGINT, &act, &oldact); 
sigaction(SIGTERM, &act, &oldact); 
sigaction(SIGTSTP, &act, &oldact); 
act.sa_flags = 0; 

在此之後我運行一個for循環來收集輸入並打印回來了(基本上表現得像貓)。

char *linebuf = NULL; 
size_t n = 0; 
int len; 
while(1){ 
    len = getline(&linebuf, &n, stdin); 
    if(len>0){ 
     printf("%s", linebuf); 
    } 
} 

然而,一旦從我處理的信號返回,getline()不再塊輸入,而是始終返回-1同時設定errnoEINTR這是一箇中斷的系統調用。我顯然打算打斷getline(),但我該如何重置它,以便我可以繼續閱讀輸入?

有幾個類似的問題並沒有真正解決我的問題,但它可以幫助您更好地理解問題。 12

另一個有趣的花絮是,我是用signal()我的信號處理時,我沒有這個問題,但我改sigaction()因爲它是POSIX兼容。

+1

'的printf(linebuf);'可能不是一個有效的C語法。 – haccks

+1

@haccks,是的,它應該已經'的printf( 「%S」,linebuf);' – Shahbaz

+0

我已經改變了它是'的printf( 「%S」,linebuf);''不過的printf(linebuf);'是有效的c語法(確實給出警告'警告:格式不是字符串文字和格式參數[-Wformat-security],但它不是錯誤),而且不是問題。 – Connor

回答

4

您鏈接到的其中一個問題實際上有您的答案。但首先,有幾個參考。請注意,getline在內部使用其他功能(可能是read)。

read手冊頁:

如果讀()由信號讀取任何數據之前中斷,它應返回-1,並將errno設置爲[EINTR]。

...

...有一個額外的功能,選擇(),其目的是暫停,直到指定的活性(數據閱讀,空間寫,等等)上指定的文件中檢測描述。在希望在由於信號引起的I/O中斷的情況下(例如鍵盤輸入)在read()之前使用select()時,在這些系統中編寫的應用程序中很常見。

select手冊頁:

的PSELECT()函數將檢查文件描述符集合......看到他們的一些描述符是否已準備好讀,準備好寫,或者分別有待處理的特殊情況。

因此,您可以使用select來了解何時可以安全開始閱讀。但是,這仍然不會阻止read稍後中斷。 (我剛剛說過這是你的信息)。

要實際清除流的錯誤標誌,將其作爲this answer添加到您的鏈接問題狀態(儘管在C++中),您需要使用clearerr函數清除流上的任何錯誤標誌。請注意,手冊頁(以及C標準)沒有具體說明它也會清除「中斷」標誌,但這僅僅是因爲超出了C的範圍,並且處於POSIX領域。


作爲一個方面說明,我想說,請不要使用memset清除一個結構。 C有那麼這是一種非常好的方式:

struct sigaction act = {0}; 

或者,如果你想成爲迂腐,因爲struct sigaction第一要素可能是另一種結構或數組,可以分配給其規定的領域之一:

struct sigaction act = { .sa_handler = NULL }; 
2

一個很晚的答案,但我會分享一些我最近學到的處理這個問題。當您在sigaction結構中將sa_flags設置爲0時,您在獲取信號時清除了用於讀取的默認行爲。當您使用舊的signal()方法時,該默認行爲將被保留。爲了解決這個問題,你必須設置sa_flags = SA_RESTART。在這種情況下,信號處理程序將被調用,並且如果getline不在讀取過程中,getline將繼續等待輸入。如果信號出現在讀取中間,您仍然會得到-1,並且在恢復之前您必須清除()流。但是,如果讀取被阻塞等待數據併發生信號,使用SA_RESTART,在信號處理程序成功執行後立即返回讀取。

我個人希望能夠中斷函數getline()這樣我就可以很好地終止程序等待管道和遇到了麻煩,因爲我用的是舊的信號()方法來設置我的處理程序。所以我會得到信號,但getline永遠不會被打斷,因爲讀取被阻塞等待數據,並且它會在我的信號處理程序執行後自動重新啓動讀取。當我移動到sigaction的結構,並設置其中sa_flags = 0時,中斷,則對getline使其基本上返回EINTR,我可以將信號發送到我的過程把這件事告訴關機。

我喜歡這個論壇。我不能指望它幫助解決棘手問題的次數。我以爲我會回報。希望這有助於下一個程序員!