signal(SIGPIPE, SIG_IGN)

工程師WWW發表於2014-08-28

signal(SIGPIPE, SIG_IGN)  

當伺服器close一個連線時,若client端接著發資料。
根據TCP 協議的規定,會收到一個RST響應,client再往這個伺服器傳送資料時,系統會發出一個SIGPIPE訊號給程式,告訴程式這個連線已經斷開了,不要再寫了。 
根據訊號的預設處理規則SIGPIPE訊號的預設執行動作是terminate(終止、退出),所以client會退出。
若不想客戶端退出可以把SIGPIPE設為SIG_IGN 
如:    signal(SIGPIPE,SIG_IGN); 
這時SIGPIPE交給了系統處理。 
伺服器採用了fork的話,要收集垃圾程式,防止殭屍程式的產生,可以這樣處理: 
signal(SIGCHLD,SIG_IGN); 交給系統init去回收。 
這裡子程式就不會產生殭屍程式了。 
 
============
寫了一個伺服器程式,在Linux下測試,然後用C++寫了客戶端用千萬級別數量的短連結進行壓力測試.  但是伺服器總是莫名退出,沒有core檔案.

最後問題確定為, 對一個對端已經關閉的socket呼叫兩次write, 第二次將會生成SIGPIPE訊號, 該訊號預設結束程式.

具體的分析可以結合TCP的"四次握手"關閉. TCP是全雙工的通道, 可以看作兩條單工通道, TCP連線兩端的兩個端點各負責一條.

當對端呼叫close時, 雖然本意是關閉整個兩條通道, 但本端只是收到FIN包.

按照TCP協議的語義, 表示對端只是關閉了其所負責的那一條單工通道, 仍然可以繼續接收資料.

也就是說, 因為TCP協議的限制, 一個端點無法獲知對端的socket是呼叫了close還是shutdown.

對一個已經收到FIN包的socket呼叫read方法, 如果接收緩衝已空, 則返回0, 這就是常說的表示連線關閉.

但第一次對其呼叫write方法時, 如果傳送緩衝沒問題, 會返回正確寫入(傳送).

但傳送的報文會導致對端傳送RST報文, 因為對端的socket已經呼叫了close, 完全關閉, 既不傳送, 也不接收資料.

所以, 第二次呼叫write方法(假設在收到RST之後), 會生成SIGPIPE訊號, 導致程式退出.

為了避免程式退出, 可以捕獲SIGPIPE訊號, 或者忽略它, 給它設定SIG_IGN訊號處理函式:

signal(SIGPIPESIG_IGN);

這樣, 第二次呼叫write方法時, 會返回-1, 同時errno置為SIGPIPE. 程式便能知道對端已經關閉.

 

linux下寫socket的程式的時候,如果嘗試send到一個disconnected socket上,就會讓底層丟擲一個SIGPIPE訊號。

這個訊號的預設處理方法是退出程式,大多數時候這都不是我們期望的。因此我們需要過載這個訊號的處理方法。

呼叫以下程式碼,即可安全的遮蔽SIGPIPE

signal(SIGPIPE, SIG_IGN);

我的程式產生這個訊號的原因是:  client端通過 pipe 傳送資訊到server端後,就關閉client端, 這時server端,返回資訊給 client 端時就產生Broken pipe 訊號了,伺服器就會被系統結束了。

 

對於產生訊號,我們可以在產生訊號前利用方法 signal(int signum, sighandler_t handler) 設定訊號的處理。

如果沒有呼叫此方法,系統就會呼叫預設處理方法:中止程式,顯示提示資訊(就是我們經常遇到的問題)。

我們可以呼叫系統的處理方法,也可以自定義處理方法。 
系統裡邊定義了三種處理方法: 

(1)SIG_DFL訊號專用的預設動作:   

(a)如果預設動作是暫停執行緒,則該執行緒的執行被暫時掛起。

當執行緒暫停期間,傳送給執行緒的任何附加訊號都不交付,直到該執行緒開始執行,但是SIGKILL除外。   

(b)把掛起訊號的訊號動作設定成SIG_DFL,且其預設動作是忽略訊號 (SIGCHLD)。

(2)SIG_IGN忽略訊號   

(a)該訊號的交付對執行緒沒有影響  

(b)系統不允許把SIGKILL或SIGTOP訊號的動作設定為SIG_DFL 3)SIG_ERR   


專案中我呼叫了signal(SIGPIPESIG_IGN), 這樣產生  SIGPIPE 訊號時就不會中止程式,直接把這個訊號忽略掉。

相關文章