Канал FIFO всегда читается в select()

В псевдокоде 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 был прочитан один раз, как сделать так, чтобы его считали нечитаемым до следующей записи?)

3 ответа

Решение

Вы открыли этот FIFO как доступный только для чтения (O_RDONLY), когда в FIFO нет записывающего устройства, конец чтения получит EOF,

Выберите системный вызов вернется на EOF и для каждого EOF вы справитесь, будет новый EOF, Это причина наблюдаемого поведения.

Чтобы избежать этого, откройте этот FIFO для чтения и записи (O_RDWR). Это гарантирует, что у вас есть по крайней мере один писатель на FIFO, таким образом, не будет EOF и в результате выберите не вернется, если кто-то пишет в этот FIFO.

Простой ответ - читать до read() возвращается EWOULDBLOCK (или же EAGAIN) или выкидывает с ошибкой.

То, что вы говорите, просто не может произойти, если используемая вами операционная система (или среда выполнения) не глючит. В противном случае вы должны делать что-то не так. Например, select() использует триггерный ввод-вывод. Я думаю, что, скорее всего, вы не сливаете сокет полностью, и так 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;
}

По сути, триггерный ввод-вывод означает, что вы постоянно получаете уведомление о том, что есть что прочитать, даже если вы уже были уведомлены об этом раньше. Напротив, инициируемый фронтом ввод / вывод означает, что вы получаете уведомление только один раз каждый раз, когда поступают новые данные, и не имеет значения, читаете ли вы их или нет. select() является интерфейсом ввода-вывода, управляемым уровнем

Надеюсь, поможет. Удачи!

У меня возникла такая проблема, предположительно из-за ошибки операционной системы. Я обнаружил, что закрытие и повторное открытие fifo после каждого возврата из вызова select решило проблему.

Другие вопросы по тегам