Канал 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 решило проблему.