简体   繁体   English

UNIX网络编程书中的等待功能?

[英]wait function in the book of unix network programming?

In chapter 5 in the book Unix Network Programming by Stevens et al, there are a server program and a client program as follows: 在史蒂文斯等人的《 Unix网络编程 》一书的第5章中,有一个服务器程序和一个客户端程序,如下所示:

server 服务器

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);
}

function sig_child works to handle signal SIGCHLD; sig_child函数可处理信号SIGCHLD; the code is as follows: 代码如下:

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;
}

client 客户

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);

As you can see in the source code of the client, the program will establish five connections to the server end, but only one connection is used in the program; 如您在客户端的源代码中所见,该程序将建立到服务器端的五个连接,但是程序中仅使用一个连接。 after str_cli is finished, exit(0) is called. str_cli完成后,将调用exit(0) And all the connections should be closed, then the five child processes in the server will exit, and SIGCHLD is sent to the parent process, which uses function sig_child to handle the SIGCHLD. 并且应该关闭所有连接,然后服务器中的五个子进程将退出,并将SIGCHLD发送到父进程,该父进程使用函数sig_child来处理SIGCHLD。 Then the while loop will confirm all the child processes will be waited by parent process correctly. 然后while循环将确认父进程正确等待所有子进程。 And I test the program for a couple of times; 而且我对该程序进行了几次测试。 it works well, all the children will be cleaned. 效果很好,所有孩子都会被打扫干净。

But in the book, the authors wrote that "wait could not work properly, because function wait may become blocked before all the child processes exit" . 但是在书中,作者写道:“等待无法正常工作,因为功能等待可能在所有子进程退出之前就被阻塞了” So is the statement right in the book? 那么书中的陈述正确吗? If it's right, could you please explain it in more detail. 如果正确的话,请您详细解释一下。 (PS: I think wait in a while statement would properly handle the exit of all the child processes.) (PS:我觉得waitwhile声明将妥善处理所有的子进程的退出。)

My two cents about what author meant with that statement. 我的两分钱是关于作者那句话的意思。

SIGCHLD gets raised in following three conditions SIGCHLD在以下三个条件下得到提高

1) Child exits 2) Child interrupted 3) Child continued. 1)儿童退出2)儿童被打扰3)儿童继续。

In case of 2 and 3 wait will be blocked because child has yet not exited. 在2和3的情况下,等待将被阻止,因为孩子尚未退出。

The problem is not with wait , but with signal delivery. 问题不在于wait ,而是与信号传递有关。 The sig_chld function in the book doesn't have the while loop, it only waits for one child 本书中的sig_chld函数没有while循环,它仅等待一个孩子

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

When the client exits, all connections are closed and all the children eventually terminate. 当客户端退出时,所有连接都将关闭,所有子代最终都将终止。 Now, the first SIGCHLD signal is delivered and upon entering the signal handler, the signal is blocked. 现在,第一个SIGCHLD信号被传递,并且进入信号处理程序后,该信号将被阻塞。 Any further signal won't be queued and is therefore lost, causing zombie children in the server. 任何其他信号都不会排队,因此会丢失,从而导致服务器中的僵尸儿童。

You can fix this by wrapping wait in some loop, as you did. 您可以像以前那样通过将wait包装在某个循环中来解决此问题。 Another solution is to ignore SIGCHLD explicitly, which is valid when you don't need the exit status of your children. 另一种解决方案是显式地忽略SIGCHLD ,这在您不需要孩子的退出状态时有效。


While wait in a loop finally waits for all children, it has the drawback, that wait blocks, if there are still children running. 虽然wait的循环终于等待所有的孩子,它有缺点,那wait块,如果有仍在运行的孩子。 This means the process is stuck in the signal handler until all children are terminated. 这意味着直到所有子级都终止之前,进程一直停留在信号处理程序中。

The solution in the book is to use waitpid with option WNOHANG in a loop 书中的解决方案是在循环中使用带有选项WNOHANG waitpid

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

This loop waits for all terminated children, but exits as soon as possible, even if there are running children. 此循环等待所有终止的子代,但即使有正在运行的子代也要尽快退出。


To reproduce the server hanging in the signal handler, you must do the following 要重现挂在信号处理程序中的服务器,您必须执行以下操作

  • start server 启动服务器
  • start first client 开始第一个客户
  • start second client 启动第二个客户
  • close one of the clients 关闭一位客户
  • start a third client 启动第三个客户
  • enter text in the third client 在第三个客户端中输入文本
    You won't get a response 你不会得到回应

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM