我們調用wait或waitpid來處理已終止的子程式。
#include <sys/types.h>
#include <sys/wait.h>
pid_t wait(int *status);
pid_t waitpid(pid_t pid, int *status, int options);
函數wait和waitpid均傳回2個值,已終止的子程序的PID,以及出參傳回的INT型終止狀态。我們可以調用宏函數,來檢查終止狀态,可以辨識子程序是正常終止,由某個信号殺死,還是由作業控制停止。如果調用wait的程序,沒有已終止子程序,那麼wait将阻塞直到有一個子程序終止。waitpid函數給我們更多的控制,比如等待哪個程序,是否阻塞。其中,pid參數允許我們指定想等待的程序ID,-1表示等待第一個終止的子程序。option選項設定為
WNOHANG
,則告訴核心在沒有已終止子程序時不要阻塞。
函數wait和waitpid有什麼差別?
用戶端與伺服器建立5個TCP連接配接:
#include "unp.h"
int
main(int argc, char **argv)
{
int i, sockfd[5];
struct sockaddr_in servaddr;
if (argc != 2)
err_quit("usage: tcpcli <IPaddress>");
for (i = 0; i < 5; i++) {
sockfd[i] = Socket(AF_INET, SOCK_STREAM, 0);
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_port = htons(SERV_PORT);
Inet_pton(AF_INET, argv[1], &servaddr.sin_addr);
Connect(sockfd[i], (SA *) &servaddr, sizeof(servaddr));
}
str_cli(stdin, sockfd[0]); /* do it all */
exit(0);
}
當客戶終止時,所有打開的描述符由核心自動關閉,且所有5個連接配接基本在同一時刻終止。這樣引發了5個FIN封包,每個連接配接一個,導緻伺服器的5個子程序在同一時刻終止,基本在同一時刻引發了5個SIGCHLD信号遞交給父程序。
- 第一次咱們跑,5個子程序終止引發的5個SIGCHLD信号都處理了
- 第二次咱們跑,5個子程序終止引發的2個SIGCHLD型号被處理,剩下3個變成僵屍程序

- 為什麼會出現這種不确定事件呢?
因為建立一個信号處理函數并在其中調用wait并不能完全防止出現僵屍程序,這個問題在于:5個信号都是信号處理函數執行之前産生,而信号處理函數隻執行一次,而産生的信号一般又不排隊,第一個信号還沒執行結束,第二個相同的信号就到達了,導緻後面一些子程序沒法wait。
- 有什麼解決辦法呢?
正确的解決辦法是調用waitpid代替wait。我們在一個循環裡調用waitpid,以擷取所有已終止子程序的狀态,我們必須指定
WNOHANG
選項,它告訴waitpid,在有尚未終止的子程序運作時不要阻塞。
#include "unp.h"
void
sig_chld(int signo)
{
pid_t pid;
int stat;
int i = 0;
while ( (pid = waitpid(-1, &stat, WNOHANG)) > 0)
{
printf("i = %dtchild %d terminatedn", ++i,pid);
}
return;
}
嘿嘿嘿,現在是不是輸入EOF之後,所有的僵屍程序都會被幹掉了呀~
為了更清晰的檢視效果,我自己加了一個序号i,先看第一次運作結果:
- 前面2個信号,都分别觸發了信号處理函數。
- 後面3個信号,隻觸發一次信号處理函數。
簡單來說,第一個信号被捕獲,處理函數執行完後,第二個信号還沒有被捕獲到,waitpid直接傳回了0。第二個信号被捕獲,同第一個一樣。第三個信号被捕獲,處理函數執行完後,發現剩下的2個信号也被捕獲了,是以while循環繼續。
再看第二次運作結果,正好反過來了。正确的服務端代碼如下:
#include "unp.h"
int
main(int argc, char **argv)
{
int listenfd, connfd;
pid_t childpid;
socklen_t clilen;
struct sockaddr_in cliaddr, servaddr;
void sig_chld(int);
listenfd = Socket(AF_INET, SOCK_STREAM, 0);
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(SERV_PORT);
Bind(listenfd, (SA *) &servaddr, sizeof(servaddr));
Listen(listenfd, LISTENQ);
Signal(SIGCHLD, sig_chld); /* must call waitpid() */
for ( ; ; ) {
clilen = sizeof(cliaddr);
if ( (connfd = accept(listenfd, (SA *) &cliaddr, &clilen)) < 0) {
if (errno == EINTR)
continue; /* back to for() */
else
err_sys("accept error");
}
if ( (childpid = Fork()) == 0) { /* child process */
Close(listenfd); /* close listening socket */
str_echo(connfd); /* process the request */
exit(0);
}
Close(connfd); /* parent closes connected socket */
}
}
參考文獻:《UNIX網絡程式設計 卷1:套接字聯網API》