FIFO管道在select()中始终可读

时间:2013-01-30 00:11:15

标签: c select file-descriptor fifo

在C伪代码中:

while (1) {
    fifo = open("fifo", O_RDONLY | O_NONBLOCK);
    fd_set read;
    FD_SET(fifo, &read);
    select(nfds, &read, NULL, NULL, NULL);
}

进程在select()触发时休眠,直到另一个进程写入fifo。之后,它总是会找到fifo作为可读文件描述符。

如何避免这种行为(也就是说,在fifo被读取一次之后,如何让它在被另一次写入之前被发现是不可读的?)

2 个答案:

答案 0 :(得分:31)

您将该FIFO打开为只读(O_RDONLY),只要FIFO没有写入器,读取端就会收到EOF

选择系统调用将在EOF上返回,对于您处理的每个EOF,都会有一个新的EOF。这是观察到的行为的原因。

为了避免这种情况,打开FIFO进行读写(O_RDWR)。这样可以确保FIFO上至少有一个写入器,因此不会有EOF,因此除非有人写入该FIFO,否则select不会返回。

答案 1 :(得分:3)

简单的答案是在read()返回EWOULDBLOCK(或EAGAIN)之前阅读,或者在错误时解读。

除非您使用的操作系统(或运行时)有问题,否则您所说的内容根本无法发生。否则你一定做错了。例如,select()正在使用级别触发的I / O.我认为,最有可能的是,你没有完全耗尽套接字,所以select()总是表明你还有一些东西(边缘触发事件通知不会发生这种情况)。

下面是一个简单的示例,显示了在read()返回EWOULDBLOCK之前应该如何阅读,以避免描述符处于可读状态(我已经在OS X上编译和测试了这个,而且大多数也没有错误检查,但你应该明白这一点:

/*
 * FIFO example using select.
 *
 * $ mkfifo /tmp/fifo
 * $ clang -Wall -o test ./test.c
 * $ ./test &
 * $ echo 'hello' > /tmp/fifo
 * $ echo 'hello world' > /tmp/fifo
 * $ killall test
 */

#include <sys/types.h>
#include <sys/select.h>
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>

int main()
{
    int fd;
    int n;
    fd_set set;
    ssize_t bytes;
    size_t total_bytes;
    char buf[1024];

    fd = open("/tmp/fifo", O_RDWR | O_NONBLOCK);
    if (fd == -1) {
        perror("open");
        return EXIT_FAILURE;
    }

    FD_ZERO(&set);
    FD_SET(fd, &set);

    for (;;) {
        n = select(fd+1, &set, NULL, NULL, NULL);
        if (!n)
            continue;
        if (n == -1) {
            perror("select");
            return EXIT_FAILURE;
        }
        if (FD_ISSET(fd, &set)) {
            printf("Descriptor %d is ready.\n", fd);
            total_bytes = 0;
            for (;;) {
                bytes = read(fd, buf, sizeof(buf));
                if (bytes > 0) {
                    total_bytes += (size_t)bytes;
                } else {
                    if (errno == EWOULDBLOCK) {
                        /* Done reading */
                        printf("done reading (%lu bytes)\n", total_bytes);
                        break;
                    } else {
                        perror("read");
                        return EXIT_FAILURE;
                    }
                }
            }
        }
    }

    return EXIT_SUCCESS;
}

基本上,级别触发的I / O意味着即使您之前可能已经收到通知,也会一直收到通知。相反,边缘触发的I / O意味着每次新数据到达时您只会收到一次通知,无论您是否读过它都无关紧要。 select()是一个级别触发的I / O接口。

希望它有所帮助。祝你好运!