我应该在 poll() 循环中调用 close() 吗

问题描述 投票:0回答:1

下面的程序准备使用

fork()
/
exec()
/
poll()
组合从子进程中捕获 stdout 和 stderr。它大部分工作正常,但在这里失败了:

if (close(pfds[j].fd) == -1)
    perror("close");

如果子进程打印了很多输出。错误是:“关闭:错误的文件描述符”。而且,这个错误不会每次都出现。假设我运行该程序 10 次,它可能只显示 5 次“关闭:错误的文件描述符”。这让我一直在思考——我是否应该将这个

close()
保留在
poll()
循环中?

(注意:原程序可以在这里找到。为了简单起见,所有不相关的错误处理都被删除了。)

int exec(char* argv1) {
    int pipefd_out[2], pipefd_err[2];

    pipe(pipefd_out);
    pipe(pipefd_err);
    
    pid_t child_pid = fork();

    if (child_pid == 0) {
        close(pipefd_out[0]);
        close(pipefd_err[0]);
        dup2(pipefd_out[1], STDOUT_FILENO);
        dup2(pipefd_err[1], STDERR_FILENO);

        // Prepared a few possible cases, to demo different behaviors
        if (atoi(argv1) == 0) {
            execl("./sub.out", "./sub.out", NULL);
        } else if (atoi(argv1) == 1) {
            const char *const  args[] = {"./sub.out", "segfault", NULL};
            execv(args[0], args);
        } else if (atoi(argv1) == 2) {
            const char *const  args[] = {"./sub.out", "flooding", NULL};
            execv(args[0], args);
        } else if (atoi(argv1) == 3) {
            const char *const  args[] = {"/bin/ls", "-l", "/tmp/", NULL};
            execv(args[0], args);
        } else {            
            const char *const  args[] = {"/bin/ls", "-l",
                "/path/that/definitely/does/not/exist/", NULL};
            execv(args[0], args);
        }
        perror("execl()/execv()");        
        _exit(EXIT_FAILURE);
    }
    
    close(pipefd_out[1]);
    close(pipefd_err[1]);

    struct pollfd pfds[] = {
        { pipefd_out[0], POLLIN, 0 },
        { pipefd_err[0], POLLIN, 0 },
    };
    int nfds = sizeof(pfds) / sizeof(struct pollfd);    
    int num_open_fds = nfds;

    while (num_open_fds > 0) {
        int ready = poll(pfds, nfds, -1);
        if (ready == -1)
            perror("poll()");
        for (int j = 0; j < nfds; j++) {            
            if (pfds[j].revents != 0) {
                char buf[4096] = {0};
                if (pfds[j].revents & POLLIN) {
                    ssize_t s = read(pfds[j].fd, buf, sizeof(buf)-1);
                    if (s == -1)
                        perror("read()");
                    if (j == 0) { printf("<stdout>%s</stdout>\n", buf); }
                    else { printf("<stderr>%s</stderr>\n", buf); }
                    fflush(stdout);
                } else {                /* POLLERR | POLLHUP */
                    if (close(pfds[j].fd) == -1)
                        perror("close");
                    num_open_fds--;
                }
            }
        }
    }

    int status;
    waitpid(child_pid, &status, 0);
    if (WIFEXITED(status)) {
        printf("Child process exited normally, rc: %d\n", WEXITSTATUS(status));
    } else {
        printf("Child process exited unexpectedly\n");
    }
    return EXIT_SUCCESS;
}

int main(int argc, char** argv) {
    if (argc != 2) {
        printf("Usage: %s <0|1|2|3|4>\n", argv[0]);
        return EXIT_FAILURE;
    }
    return exec(argv[1]);    
}
c pipe posix
1个回答
1
投票

描述符

pfds[j].fd
是您的管道之一。如果你得到一个错误或者它被关闭那么你需要关闭它,但是你也不应该在下一次迭代中继续将它传递给
poll

并考虑一下

pfds[j].fd == pipefd_out[0]
发生的情况。您需要重新排列阵列以移除封闭的管道。或者在
poll
.

之前的循环内重新创建数组

也许是这样的:

if (close(pfds[j].fd) == -1)
    perror("close");
num_open_fds--;
if (pfds[j] == pipefd_out[0])
    pipefd_out[0] = -1;  // This descriptor is now closed
if (pfds[j] == pipefd_err[0])
    pipefd_err[0] = -1;  // This descriptor is now closed

poll
之前创建数组:

size_t pfd_index = 0;
struct pollfd pfds[num_open_fds];
if (pipefd_out[0] != -1)
    pfds[pfd_index++] = (struct pollfd){ pipefd_out[0], POLLIN, 0 };
if (pipefd_err[0] != -1)
    pfds[pfd_index] = (struct pollfd){ pipefd_err[0], POLLIN, 0 };

int ready = poll(pfds, num_open_fds, -1);
// Etc...
© www.soinside.com 2019 - 2024. All rights reserved.