Linux с именем fifo неблокирующим read select возвращает поддельные read_fds - PullRequest
0 голосов
/ 25 августа 2018

Похоже на проблему , заданную некоторое время назад в ядре 3.x , но я вижу ее на 4.9.37.Названный fifo создается с mkfifo -m 0666.На стороне чтения он открывается с помощью

int fd = open(FIFO_NAME, O_RDONLY | O_NONBLOCK);

Полученный fd передается в вызов select().Все работает нормально, пока я не бегу echo >> <fifo-name>.

Теперь fd появляется в read_fds после возврата select().read() на fd вернет один байт данных.Все идет нормально.

В следующий раз, когда вызывается select() и возвращается, fd все еще появляется в read_fds, но read() всегда будет возвращать ноль, то есть без данных.Фактически сторона чтения будет потреблять 100% мощности процессора.Это точно та же проблема, что и наблюдаемый вопрос.

Кто-нибудь видел такую ​​же проблему?И как это можно решить или обойти должным образом?

Я понял, если я закрою конец чтения fifo и снова открою его, он будет работать правильно.Это, вероятно, нормально, потому что мы не отправляем много данных.Хотя это не хороший или общий обходной путь.

1 Ответ

0 голосов
/ 25 августа 2018

Это ожидаемое поведение, потому что регистр конца ввода приводит к тому, что read() не блокируется;он немедленно возвращает 0.

Если вы посмотрите на man 2 select , это ясно говорит о том, что дескриптор в readfds установлен, если read() в этом дескрипторе не будет блокироваться (привремя вызова select()).

Если вы использовали poll(), он также немедленно возвратился бы с POLLHUP в revents.


Как отмечает OP, правильным обходным решением является повторное открытие FIFO.

Поскольку ядро ​​Linux поддерживает ровно один объект внутреннего канала для представления каждого открытого FIFO (см. man 7 fifo и man 7 pipe ), надежный подход в Linux состоит в том, чтобы открывать другой дескриптор для FIFO всякий раз, когда встречается конец ввода (read() возвращает 0), и закрывать оригинал.В то время, когда открыты оба дескриптора , , они ссылаются на один и тот же объект канала ядра, поэтому нет гоночного окна или риска потери данных.

В псевдо-C:

fifoflags = O_RDONLY | O_NONBLOCK;
fifofd = open(fifoname, fifoflags);
if (fifofd == -1) {
    /* Error checking */
}

/* ... */

/* select() readfds contains fifofd, or
   poll() returns POLLIN for fifofd: */

    n = read(fifofd, buffer, sizeof buffer)
    if (!n) {
        int tempfd;

        tempfd = open(fifopath, fifoflags);
        if (tempfd == -1) {
            const int cause = errno;
            close(fifofd);

            /* Error handling */

        }
        close(fifofd);
        fifofd = tempfd;

        /* A writer has closed the FIFO. */

    } else
        /* Handling for the other read() result cases */

Политика выделения файловых дескрипторов в Linux такова, что tempfd будет бесплатным дескриптором с наименьшим номером.

В моей системе (ноутбук Core i5-7200U), повторно открыв FIFO в этомпуть занимает не более 1,5 мкс.То есть это может быть сделано около 680 000 раз в секунду.Я не думаю, что это открытие является узким местом для любого разумного сценария, даже на маломощных встроенных машинах Linux.

...