linux僵死程序與併發伺服器程式設計
序
僵死(zombie)程序簡而言之就是:子程序退出時,父程序並未對其發出的SIGCHILD訊號進行適當處理,導致子程序停留在僵死狀態等待其父程序為其收屍,這個狀態下的子程序就是僵死程序。
因為併發伺服器常常fork很多子程序,子程序終結之後需要伺服器程序去wait清理資源。對於某些程序,特別是伺服器程序往往在請求到來時生成子程序處理請求。如果父程序不等待子程序結束,子程序將成為殭屍程序(zombie)從而佔用系統資源。如果父程序等待子程序結束,將增加父程序的負擔,影響伺服器程序的併發效能。
查詢所有的僵死程序
ps -A -o stat,ppid,pid,cmd | grep -e '^[Zz]'
處理僵死程序
最簡單的方法忽略SIGCHLD訊號讓系統處理
在Linux下呼叫signal(SIGCHLD, SIG_IGN),可以簡單地將 SIGCHLD訊號的操作設為SIG_IGN忽略。 這樣可以讓核心把殭屍子程序轉交給init程序去處理,省去了大量殭屍程序佔用系統資源。(Linux Only)
父程序waitpid釋放僵死程序
父程序在fork呼叫之前呼叫signal設定SIGCHLD訊號處理函式。SIGCHLD處理函式如下:
void sig_chld(int signo) { pid_t pid; int stat; //Unix下如果一個訊號在被阻塞期間產生一次或多次,那麼該訊號被解阻塞之後通常只遞交一次,也就是說Unix訊號預設是不排隊的。 while ((pid=waitpid(-1, &stat, WNOHANG)) > 0) { pintf("child %d terminated\n", pid); } return; }
通訊注意事項
signal(SIGPIPE, SIG_IGN);
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. 程式便能知道對端已經關閉.