天天看点

waitpid函数_wait和waitpid是干啥用的?

我们调用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个变成僵尸进程
waitpid函数_wait和waitpid是干啥用的?
  • 为什么会出现这种不确定事件呢?

因为建立一个信号处理函数并在其中调用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之后,所有的僵尸进程都会被干掉了呀~

waitpid函数_wait和waitpid是干啥用的?

为了更清晰的查看效果,我自己加了一个序号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》