程序異常退出 卻沒有產生core文件 ,但已經正確設置了ulimit
http://blog.sina.com.cn/s/blog_59b6af6901011ekd.html
可能的兩種原因:
- 緩沖區溢出等錯誤可能導致的堆棧被破壞
- 程序收到SIGPIPE等信號,進行默認退出處理
第二種情況,可用
#kill -s SIGPIPE pid
來模擬,查看程序對此類信號的處理結果
=======sigpipe
如果你在讀取一個管道、套接口、FIFO等設備時,當寫入端關閉連接時,你將會得到一個文件結束符(EOF)(read()返回零字節讀取)。
如果你試圖向一個管道或套接口寫入,當讀取方關閉連接,你將得到一個SIGPIPE的信號,它會使進程終止除非指定處理方法。
======sigchild
服務器采用了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(SIGPIPE, SIG_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(SIGPIPE, SIG_IGN), 這樣產生 SIGPIPE 信號時就不會中止程序,直接把這個信號忽略掉。
=========
What happens if the client ignores the error return from readline and writes more data to the server? This can happen, for example, if the client needs to perform two writes to the server before reading anything back, with the first write eliciting the RST.
The rule that applies is: When a process writes to a socket that has received an RST, the SIGPIPE signal is sent to the process. The default action of this signal is to terminate the process, so the process must catch the signal to avoid being involuntarily terminated.
If the process either catches the signal and returns from the signal handler, or ignores the signal, the write operation returns EPIPE.