1. 程式人生 > >linux僵死程序與併發伺服器程式設計

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. 程式便能知道對端已經關閉.