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

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

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

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

    Простым ответом является чтение, пока read() вернет EWOULDBLOCK (или EAGAIN ) или не 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  #include  #include  #include  #include  #include  #include  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() – это интерфейс ввода-вывода с уровнем запуска.

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