多進程網路編程中處理fork後可能的僵死進程和可能被中斷的系統調用問題。 ...
處理僵死進程
- 一個進程使用fork創建子進程後,如果子進程退出時,父進程並沒有調用wait或waitpid來獲取子進程的狀態信息,那麼子進程的進程描述符將一直保持在系統中。這種進程就稱為僵死進程。
- 顯然,僵死進程會占用內核空間,所以,無論何時我們fork子進程都得wait它們,以防它們變成僵死進程。
- 處理僵死進程,即捕獲SIGCHLD信號,在信號處理函數中調用wait或waitpid,即子進程退出時,在父進程中處理其退出信息。
在多進程中,正確的處理子進程僵死狀態的方法是調用waitpid而不是wait。原因在於我們在一個迴圈內waitpid,以獲得所有已終止子進程的狀態。我們必須指定WNOHANG選項(若pid指定的進程沒有結束,則waitpid立即返回0,不等待。若結束,返回其進程ID),它告知waitpid在有尚未終止的子進程在運行時不要阻塞。採用while來進行輪詢。
void sig_chld(int signo){ pid_t pid; int stat; while((pid = waitpid(-1, &stat, WNOHANG)) > 0){ printf("child %d terminated.\n", pid); } return; }
處理被中斷的系統調用
- 慢系統調用是指可能會使系統永遠阻塞的系統調用。
- 適用於慢系統調用的基本規則是:當阻塞於某個慢系統調用的一個進程捕獲某個信號且相應信號處理函數返回時,該系統調用可能會返回一個EINTR錯誤。
accept函數就屬於這類,為了處理被中斷的accept,我們常採用以下方式:
for( ; ; ){ clilen = sizeof(cliaddr); if((connfd = accept(listenfd, (struct sockaddr*)&cliaddr, &clilen)) < 0){ if(errno == EINTR) continue; else{ err_sys("accept error"); } } }
- 上述代碼所做的事就是自己重啟被中斷的系統調用。對於accept以及諸如read、write、select和open之類函數來說,都是適用的。
但是有一個函數我們不能重啟:connect。如果該函數返回EINTR,我們就不能再次調用它,否則將立即返回一個錯誤。當connect被一個捕獲的信號中斷而不自動重啟時,我們必須調用select來等待連接完成。