轮询管道后 fgetc 仍然阻塞

fgetc still blocking after polling a pipe

目前,我正在读取来自已执行的子程序的输入行。所以基本上,如果一个子程序不能正确执行,它不应该在读取时管道信息并且会抛出错误。

我尝试轮询文件描述符,它 returns 一个无论程序是否正确执行。所以基本上我通过了 poll 然后 fgetc 是 hanging/blocking 因为没有什么可读的,但是 fgetc 也没有返回 -1.

读取和轮询:

char* read_line(int fd) {
    // fd is a pipe's read end. I know it reads properly.
    FILE *file = fdopen(fd, "r"); 
    int ret;
    struct pollfd fdinfo[1];
    fdinfo[0].fd = fd;
    fdinfo[0].events = POLLIN;
    ret = poll(fdinfo,1, 1000);
    if (ret < 0) {
        return "NOPE";
    }   
    char* result = malloc(sizeof(char) * 80);
    memset(result, 0, sizeof(int));
    int position = 0;
    int next = 0;
    while (1) {
        next = fgetc(file); //STALLING HERE
        if (next == '!') {
                free(result);
                return "!";
        }
        if (next == EOF || next == '\n') {
            result[position] = '[=10=]';
            return result;
        } else {
            result[position++] = (char)next;
        }
    }
}

您没有充分检查 poll() 提供给您的信息。您确实通过 returning -1 检测到 poll() 报告错误的情况,但它这样做并不能保证有任何数据可供读取。负 return 值表示 poll() 未能完成其工作;它不会告诉您任何轮询文件描述符的状态。

首先,poll() return如果超时则为0。在那种情况下,您的代码只是继续前进并尝试从文件描述符中读取,这将阻塞,除非数据恰好在 poll() 的 return 和对 fgetc() 的调用之间到达。

其次,poll() 通过设置一个或多个相应的 revent 位,并在该 FD 上报告事件(部分由 return 一个正数)。具体来说,

poll() sets the POLLHUP, POLLERR and POLLNVAL flag in revents if the condition is true, even if the application did not set the corresponding bit in events

A positive [return] value indicates the total number of pollfd structures [...] for which the revents member is non-zero

(POSIX 1003.1-2008;已强调)

如果写入管道另一端的子进程崩溃,您很可能会看到 POLLHUP 事件。如果您的程序以某些方式搞砸了其文件描述符处理,您可能会看到一个 POLLNVAL 事件,并且您始终必须考虑 I/O 错误的可能性,该 poll() 信号作为 POLLERR 事件。

因此,为避免阻塞,您应该仅在 poll() returns 1 时才尝试从管道读取并在文件事件中发出 POLLIN 信号。可以想象,您可能会看到它与 POLLERR 或 POLLHUP 一起出现。如果有 POLLERR,我会建议中止,但是当管道的写端在任何进程中不再打开时,预计会出现 POLLHUP,并且不会使任何仍可读取的数据无效(这将由随附的 POLLIN 发出信号)。