Le tube FIFO est toujours lisible dans select ()

En 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); } 

Le processus s’active tel que déclenché par select() jusqu’à ce qu’un autre processus écrit dans fifo . Ensuite, il trouvera toujours fifo sous forme de descripteur de fichier lisible.

Comment éviter ce comportement (c’est-à-dire qu’après avoir lu une fois fifo , comment le rendre illisible jusqu’à ce qu’il reçoive une autre écriture?)

Vous avez ouvert cette FIFO en lecture seule (O_RDONLY), chaque fois qu’il n’y a pas d’écrivain dans la FIFO, l’extrémité lue recevra un EOF .

Un appel système sélectionné revient sur EOF et pour chaque EOF vous gérez, il y aura un nouvel EOF . C’est la raison du comportement observé.

Pour éviter cela, ouvrez cette FIFO en lecture et en écriture (O_RDWR). Cela garantit que vous avez au moins un graveur sur la FIFO, il n’y aura donc pas de EOF et, par conséquent, select ne reviendra pas à moins que quelqu’un n’écrit dans cette FIFO.

La réponse simple est de lire jusqu’à ce que read() retourne EWOULDBLOCK (ou EAGAIN ), ou un craps avec une erreur.

Ce que vous dites ne peut se produire que si le système d’exploitation (ou le moteur d’exécution) que vous utilisez est défectueux. Sinon, vous devez faire quelque chose de mal. Par exemple, select() utilise une E / S déclenchée par le niveau. Je pense que, très probablement, vous ne drainez pas complètement le socket, et donc select() indique toujours qu’il vous rest quelque chose (ce qui ne se produit pas avec les notifications d’événement déclenchées par un périphérique).

Voici un exemple simple qui montre comment lire jusqu’à ce que read() renvoie EWOULDBLOCK afin d’éviter de laisser le descripteur dans un état lisible (je l’ai compilé et testé sur OS X, et il n’existe généralement pas de vérification des erreurs, mais vous devrait avoir l’idée):

 /* * 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; } 

Fondamentalement, les E / S déclenchées par niveau signifient que vous êtes toujours averti s’il y a quelque chose à lire, même si vous en avez déjà été averti auparavant. Au contraire, les E / S déclenchées par la périphérie signifient que vous ne recevez une notification qu’une fois à chaque nouvelle donnée et que vous la lisiez ou pas. select() est une interface d’E / S déclenchée par le niveau.

J’espère que ça aide. Bonne chance!