c - unix网络编程书中的wait函数?

标签 c unix

书中第5章Unix Network Programming Stevens et al 有一个服务端程序和一个客户端程序如下:

服务器

mysignal(SIGCHLD, sig_child);
for(;;)
{
    connfd = accept(listenfd, (struct sockaddr *)&ca, &ca_len);

    pid = fork();
    if(pid == 0)
    {
        //sleep(60);
        close(listenfd);
        str_echo(connfd);
        close(connfd);
        exit(0);
    }
    close(connfd);
}

函数 sig_child 用于处理信号 SIGCHLD;代码如下:

void sig_child(int signo)
{
    pid_t pid;
    int stat;
    static i = 1;
    i++;
    while(1)
    {
        pid = wait(&stat);
        if(pid > 0)
        {
            printf("ith: %d, child %d terminated\n", i, pid);
        }
        else
        {
            break;
        }
    }   
    //pid = wait(&stat);
    return;
}

客户端

for(i = 0 ; i < 5; i++)
{
    sockfd[i] = socket(AF_INET, SOCK_STREAM, 0);
    if(sockfd[i] < 0)
    {
        perror("create error");
        exit(-1);
    }

    memset(&sa, 0, sizeof(sa));
    sa.sin_family = AF_INET;
    sa.sin_port = htons(5900);

    if(inet_pton(AF_INET, argv[1], &sa.sin_addr) != 1)
        {
                perror("inet_pton error");
                exit(-1);
        }
    connect(sockfd[i], (struct sockaddr *)&sa, sizeof(sa));
}
str_cli(sockfd[0], stdin);
exit(0);

在客户端的源码中可以看到,程序会与服务器端建立5个连接,但程序中只使用了1个连接; str_cli 完成后,调用 exit(0)。并且应该关闭所有连接,然后服务器中的五个子进程将退出,并将 SIGCHLD 发送给父进程,父进程使用函数 sig_child 来处理 SIGCHLD。然后 while 循环将确认所有子进程都将被父进程正确等待。我对程序进行了几次测试;效果很好,所有的 child 都会被打扫干净。

但是在书中,作者写道“wait 无法正常工作,因为函数 wait 可能在所有子进程退出之前被阻塞”。那么书上的说法对吗?如果是对的,请您详细解释一下。 (PS:我认为 while 语句中的 wait 会正确处理所有子进程的退出。)

最佳答案

问题不在于 wait,而在于信号传递。书中的sig_chld函数没有while循环,它只等待一个 child

void sig_child(int signo)
{
    pid_t pid;
    int stat;
    pid = wait(&stat);
    printf("child %d terminated\n", pid);
    return;
}

当客户端退出时,所有连接都关闭,所有子连接最终终止。现在,第一个 SIGCHLD 信号被传递,并且在进入信号处理程序时,信号被阻塞。任何进一步的信号都不会排队,因此会丢失,从而导致服务器中的僵尸 child 。

您可以通过将 wait 包装在某个循环中来解决此问题,就像您所做的那样。另一种解决方案是显式忽略 SIGCHLD,这在您不需要 child 的退出状态时有效。


虽然 wait 在一个循环中最终等待所有 child ,但它有一个缺点,即 wait 阻塞,如果还有 child 在运行。这意味着进程会卡在信号处理程序中,直到所有子进程都终止。

书上的解决方法是用waitpid在循环中使用选项 WNOHANG

while ((pid = waitpid(-1, &stat, WNOHANG)) > 0)
    printf("child %d terminated\n", pid);

这个循环等待所有终止的 child ,但会尽快退出,即使有正在运行的 child 也是如此。


要重现信号处理程序中挂起的服务器,您必须执行以下操作

  • 启动服务器
  • 开始第一个客户
  • 启动第二个客户端
  • 关闭其中一个客户
  • 开始第三个客户
  • 在第三个客户端输入文字
    你不会得到回应

关于c - unix网络编程书中的wait函数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23802864/

相关文章:

linux - linux终端上的奇怪字符

linux - 为什么 more 命令无法读取标准输入,而是从管道标准输入读取?

c - C 中的 GObject 子类化无法创建子类实例

c - c中的qsort问题

c - 在 [min,max] 范围内生成随机数

c - 编写一个返回列表中字符串位置的函数(C 代码)

perl - 将非常大的 csv 文件与公共(public)列合并

linux - 如何通过将文件名与文本文件中名称的子字符串匹配来重命名文件

C - 创建 3D 整数数组并将其初始化为零

c++ - 通过套接字发送和接收字符串的子函数