1

我在处理僵尸进程时遇到了一些麻烦。我编写了一个简单的服务器,可以在玩家之间创建井字游戏。我正在使用 select() 在多个连接的客户端之间进行多路复用。只要有两个客户端,服务器就会派生另一个执行匹配仲裁程序的进程。

问题是 select() 阻塞。因此,假设有一个匹配仲裁程序作为子进程运行并退出,如果没有传入连接,父进程将永远不会等待子进程,因为 select() 正在阻塞。

我在这里有我的代码,很抱歉,因为它很乱。

while(1) {
    if (terminate)
        terminate_program();
    FD_ZERO(&rset);
    FD_SET(tcp_listenfd, &rset);
    FD_SET(udpfd, &rset);
    maxfd = max(tcp_listenfd, udpfd);

    /* add child connections to set */
    for (i = 0; i < MAXCLIENTS; i++) {
        sd = tcp_confd_lst[i];
        if (sd > 0)
            FD_SET(sd, &rset);
        if (sd > maxfd)
            maxfd = sd;
    }

    /* Here select blocks */
    if ((nready = select(maxfd + 1, &rset, NULL, NULL, NULL)) < 0) {
        if (errno == EINTR)
            continue;
        else
            perror("select error");
    }

    /* Handles incoming TCP connections */
    if (FD_ISSET(tcp_listenfd, &rset)) {
        len = sizeof(cliaddr);
        if ((new_confd = accept(tcp_listenfd, (struct sockaddr *) &cliaddr, &len)) < 0) {
            perror("accept");
            exit(1);
        }
        /* Send connection message asking for handle */
        writen(new_confd, handle_msg, strlen(handle_msg));
        /* adds new_confd to array of connected fd's */
        for (i = 0; i < MAXCLIENTS; i++) {
            if (tcp_confd_lst[i] == 0) {
                tcp_confd_lst[i] = new_confd;
                break;
            }
        }
    }

    /* Handles incoming UDP connections */
    if (FD_ISSET(udpfd, &rset)) {

    }

    /* Handles receiving client handles */
    /* If client disconnects without entering their handle, their values in the arrays will be set to 0 and can be reused. */
    for (i = 0; i < MAXCLIENTS; i++) {
        sd = tcp_confd_lst[i];
        if (FD_ISSET(sd, &rset)) {
            if ((valread = read(sd, confd_handle, MAXHANDLESZ)) == 0) {
                printf("Someone disconnected: %s\n", usr_handles[i]);
                close(sd);
                tcp_confd_lst[i] = 0;
                usr_in_game[i] = 0;
            } else {
                confd_handle[valread] = '\0';
                printf("%s\n", confd_handle); /* For testing */
                fflush(stdout);
                strncpy(usr_handles[i], confd_handle, sizeof(usr_handles[i]));
                for (j = i - 1; j >= 0; j--) {
                    if (tcp_confd_lst[j] != 0 && usr_in_game[j] == 0) { 
                        usr_in_game[i] = 1; usr_in_game[j] = 1;
                        if ((child_pid = fork()) == 0) {
                            close(tcp_listenfd);
                            snprintf(fd_args[0], sizeof(fd_args[0]), "%d", tcp_confd_lst[i]);
                            snprintf(fd_args[1], sizeof(fd_args[1]), "%d", tcp_confd_lst[j]);
                            execl("nim_match_server", "nim_match_server", usr_handles[i], fd_args[0], usr_handles[j], fd_args[1], (char *) 0);
                        }
                        close(tcp_confd_lst[i]); close(tcp_confd_lst[j]);
                        tcp_confd_lst[i] = 0; tcp_confd_lst[j] = 0;
                        usr_in_game[i] = 0; usr_in_game[j] = 0;
                    }
                }
            }
        }
    }
}

有没有一种方法可以在 select() 阻塞时等待运行?最好没有信号处理,因为它们是异步的。

编辑:实际上,我发现 select 有一个 timeval 数据结构,我们可以指定超时。使用它是个好主意吗?

4

2 回答 2

3

我认为你的选择是:

  1. 将所有子描述符保存在全局数组中,并从信号处理程序调用 wait()。如果您在主循环中不需要孩子的退出状态,我认为这是最简单的。

  2. 代替 select,使用 pselect - 它会在接收到指定的(一组)信号后返回,在你的情况下,SIGCHLD。然后在所有子 PID 上调用 wait/WNOHANG。您需要在 pselect() 之前/之后的正确时刻阻止/取消阻止 SIGCHLD,请参见此处: http: //pubs.opengroup.org/onlinepubs/9699919799/functions/pselect.html

  3. 等待/清理来自辅助线程的子 PID。我认为这是最复杂的解决方案(重新。线程之间的同步),但既然你问了,它在技术上是可能的。

于 2015-03-25T00:41:39.980 回答
2

如果你只是想防止僵尸进程,你可以设置一个SIGCHLD信号处理程序。如果您想实际等待返回状态,您可以从信号处理程序将字节写入管道(非阻塞,以防万一),然后在select循环中读取这些字节。

有关如何处理SIGCHLD,请参阅http://www.microhowto.info/howto/reap_zombie_processes_using_a_sigchld_handler.html - 你想做类似的事情while (waitpid((pid_t)(-1), 0, WNOHANG) > 0) {}

SIGCHLD也许最好的方法是从信号处理程序向主循环发送一个字节select(非阻塞,以防万一),并在可以从管道读取字节时在waitpid循环中执行循环。select

您也可以使用signalfd文件描述符来读取SIGCHLD信号,尽管这仅适用于 Linux。

于 2015-03-25T00:27:26.190 回答