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

时间:2021-01-18 23:33:26

In C pseudo-code:

在C伪代码中:

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

The process sleeps as triggered by select() until another process writes into fifo. Afterwards it will always find fifo as a readable file descriptor.

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

How to avoid this behavior (that is, after fifo has been read once, how to make it be found as unreadable until it gets another write?)

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

2 个解决方案

#1


29  

You opened that FIFO as read only (O_RDONLY), whenever there is no writer to the FIFO, the read end will receive an EOF.

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

Select system call will return on EOF and for every EOF you handle there will be a new EOF. This is the reason for the observed behavior.

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

To avoid this open that FIFO for both reading and writing (O_RDWR). This ensures that you have at least one writer on the FIFO thus there wont be an EOF and as a result select won't return unless someone writes to that FIFO.

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

#2


3  

The simple answer is to read until read() returns EWOULDBLOCK (or EAGAIN), or craps out with an error.

简单的答案是读取,直到read()返回EWOULDBLOCK(或EAGAIN),或者错误地解决。

What you are saying simply cannot be happening unless the operating system (or runtime) that you are using is buggy. Otherwise you must be doing something wrong. For example, select() is using level-triggered I/O. I'd think that, most likely, you are not draining the socket completely, and so select() always indicates that you have something left in there (this does not happen with edge-triggered event notifications).

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

Below is a simple example that shows how one should read until the read() returns EWOULDBLOCK in order to avoid leaving descriptor in readable state (I've compiled and tested this on OS X, and there is also mostly no error checking, but you should get the idea):

下面是一个简单的例子,显示了一个人应该如何阅读,直到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;
}

Basically, level-triggered I/O means that you get notified all the time if there is something to read, even though you might have been notified of this before. On a contrary, edge-triggered I/O means that you are getting notified only once every time new data arrives and it doesn't matter whether you read it or not. select() is a level-triggered I/O interface.

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

Hope it helps. Good Luck!

希望能帮助到你。祝好运!

#1


29  

You opened that FIFO as read only (O_RDONLY), whenever there is no writer to the FIFO, the read end will receive an EOF.

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

Select system call will return on EOF and for every EOF you handle there will be a new EOF. This is the reason for the observed behavior.

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

To avoid this open that FIFO for both reading and writing (O_RDWR). This ensures that you have at least one writer on the FIFO thus there wont be an EOF and as a result select won't return unless someone writes to that FIFO.

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

#2


3  

The simple answer is to read until read() returns EWOULDBLOCK (or EAGAIN), or craps out with an error.

简单的答案是读取,直到read()返回EWOULDBLOCK(或EAGAIN),或者错误地解决。

What you are saying simply cannot be happening unless the operating system (or runtime) that you are using is buggy. Otherwise you must be doing something wrong. For example, select() is using level-triggered I/O. I'd think that, most likely, you are not draining the socket completely, and so select() always indicates that you have something left in there (this does not happen with edge-triggered event notifications).

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

Below is a simple example that shows how one should read until the read() returns EWOULDBLOCK in order to avoid leaving descriptor in readable state (I've compiled and tested this on OS X, and there is also mostly no error checking, but you should get the idea):

下面是一个简单的例子,显示了一个人应该如何阅读,直到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;
}

Basically, level-triggered I/O means that you get notified all the time if there is something to read, even though you might have been notified of this before. On a contrary, edge-triggered I/O means that you are getting notified only once every time new data arrives and it doesn't matter whether you read it or not. select() is a level-triggered I/O interface.

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

Hope it helps. Good Luck!

希望能帮助到你。祝好运!