Linux中SIGPIPE信號說明

當服務器close一個連接時,若client端接着發數據。根據TCP協議的規定,會收到一個RST響應,client再往這個服務器發送數據時,系統會發出一個SIGPIPE信號給進程,告訴進程這個連接已經斷開了,不要再寫了。

我寫了一個服務器程序,在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 信號時就不會中止程序,直接把這個信號忽略掉

原文:http://blog.chinaunix.net/uid-20135786-id-3409085.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章