Page suivante Page précédente Table des matières

9. select() : attente de données

Il est assez courant de devoir attendre des données en provenance de plusieurs sources. On utilise pour cela la fonction select() qui permet de surveiller plusieurs descripteurs simultanément.

#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>

int  select(int n, fd_set *readfds, 
                   fd_set *writefds, 
                   fd_set *exceptfds, 
                   struct timeval *timeout);

FD_CLR(int fd, fd_set *set);
FD_ISSET(int fd, fd_set *set);
FD_SET(int fd, fd_set *set);
FD_ZERO(fd_set *set);      

Cette fonction attend que des données soient prêtes à être lues sur un des descripteurs de l'ensemble readfs, ou que l'un des descripteurs de writefds soit prêt à recevoir des écritures, que des exceptions se produisent (exceptfds), ou encore que le temps d'attente timeout soit épuisé.

Lorsque select() se termine, readfds, writefds et exceptfds contiennent les descripteurs qui ont changé d'état. select() retourne le nombre de descripteurs qui ont changé d'état, ou -1 en cas de problème.

L'entier n doit être supérieur (strictement) au plus grand des descripteurs contenus dans les 3 ensembles (c'est en fait le nombre de bits significatifs du masque binaire qui représente les ensembles). On peut utiliser la constante FD_SETSIZE.

Les pointeurs sur les ensembles (ou le délai) peuvent être NULL, ils représentent alors des ensembles vides (ou une absence de limite de temps).

Les macros FD_CLR, FD_ISSET, FD_SET, FD_ZERO permettent de manipuler les ensembles de descripteurs.

9.1 Attente de données provenant de plusieurs sources

Exemple :


 
  1     /* mix.c */

  2     /*
  3        affiche les données qui proviennent de 2 fifos
  4        usage:  mix f1 f2 
  5     */

  6     #include <sys/time.h>
  7     #include <sys/types.h>
  8     #include <sys/stat.h>
  9     #include <unistd.h>
 10     #include <stdio.h>
 11     #include <stdlib.h>
 12     #include <fcntl.h>

 13     #define TAILLE_TAMPON 128

 14     /*************************************************
 15        Mixe les données en provenance de deux
 16        descripteurs
 17     **************************************************/

 18     void mixer(int fd1, int fd2, int sortie)
 19     {
 20             fd_set ouverts;         /* les descripteurs ouverts */
 21             int nbouverts;

 22             FD_ZERO(&ouverts);
 23             FD_SET(fd1, &ouverts);
 24             FD_SET(fd2, &ouverts);
 25             nbouverts = 2;

 26             while (nbouverts > 0) {      /* tant qu'il reste des 
 27                                        descripteurs ouverts.... */
 28                     fd_set prets;
 29                     char tampon[TAILLE_TAMPON];

 30                     /* on attend qu'un descripteur soit prêt ... */
 31                     prets = ouverts;
 32                     if (select(FD_SETSIZE, &prets, NULL, NULL, NULL) < 0) {
 33                             perror("select");
 34                             exit(EXIT_FAILURE);
 35                     }

 36                     if (FD_ISSET(fd1, &prets)) {        /* si fd1 est prêt... */
 37                             int n = read(fd1, tampon, TAILLE_TAMPON);
 38                             if (n >= 0) {
 39                                     /* on copie ce qu'on a lu */
 40                                     write(sortie, tampon, n);
 41                             } else {
 42                                     /* fin de fd1 : on l'enlève */
 43                                     close(fd1);
 44                                     nbouverts--;
 45                                     FD_CLR(fd1, &ouverts);
 46                             }
 47                     };
 48                     if (FD_ISSET(fd2, &prets)) {        /* si fd2 est prêt... */
 49                             int n = read(fd2, tampon, TAILLE_TAMPON);
 50                             if (n >= 0) {
 51                                     /* on copie ce qu'on a lu */
 52                                     write(sortie, tampon, n);
 53                             } else {
 54                                     /* fin de fd2 : on l'enlève */
 55                                     close(fd2);
 56                                     nbouverts--;
 57                                     FD_CLR(fd2, &ouverts);
 58                             }
 59                     };
 60             };
 61     }

 62     int main(int argc, char *argv[])
 63     {
 64             int fd1, fd2;

 65             if (argc != 3) {
 66                     fprintf(stderr, "Usage : %s f1 f2\n", argv[0]);
 67                     exit(EXIT_FAILURE);
 68             };

 69             fd1 = open(argv[1], O_RDONLY);
 70             if (fd1 == -1) {
 71                     fprintf(stderr, "Ouverture %s refusée\n", argv[1]);
 72                     exit(EXIT_FAILURE);
 73             };

 74             fd2 = open(argv[2], O_RDONLY);
 75             if (fd2 == -1) {
 76                     fprintf(stderr, "Ouverture %s refusée\n", argv[2]);
 77                     exit(EXIT_FAILURE);
 78             };

 79             mixer(fd1, fd2, 1);
 80             exit(EXIT_SUCCESS);
 81     }
 

9.2 Attente de données avec délai maximum

L'exemple suivant montre comment utiliser la limite de temps dans le cas (fréquent) d'attente sur un seul descripteur.

Une fonction utilitaire :


 
  1     /*
  2       SelectFifo/AttenteDonnees.c

  3       attente de données provenant d'un descripteur ouvert,
  4       avec délai maximum
  5     */

  6     #include <sys/time.h>
  7     #include <sys/types.h>
  8     #include <sys/stat.h>
  9     #include <unistd.h>
 10     #include <stdio.h>
 11     #include <stdlib.h>
 12     #include <fcntl.h>

 13     /*
 14       fonction AttenteDonnees

 15       paramètres:
 16       - un descripteur ouvert
 17       - une durée d'attente en millisecondes

 18       rôle: attend que des données arrivent sur le descripteur pendant
 19       un certain temps.

 20       retourne:
 21       1  si des données sont arrivées
 22       0  si le délai est dépassé
 23       -1 en cas d'erreur. Voir variable errno.
 24     */

 25     int AttenteDonnees(int fd, int millisecondes)
 26     {
 27             fd_set set;
 28             struct timeval delai;
 29             FD_ZERO(&set);
 30             FD_SET(fd, &set);
 31             delai.tv_sec = millisecondes / 1000;
 32             delai.tv_usec = (millisecondes % 1000) * 1000;
 33             return select(FD_SETSIZE, &set, NULL, NULL, &delai);
 34     }
 

Le programme principal :


 
  1     /*
  2       SelectFifo/lecteur.c

  3       Exemple de lecture avec délai (timeout).
  4       M. Billaud, Septembre 2002

  5       Ce programme attend des lignes de texte provenant d'une fifo, et les
  6       affiche.
  7       En attendant de recevoir les lignes, il affiche une petite étoile
  8       tournante (par affichage successif des symboles  - \ | et /). 

  9       Exemple d'utilisation:
 10       - créer une fifo :  mkfifo /tmp/mafifo
 11       - dans une fenêtre, lancer : lecteur /tmp/mafifo
 12       le programme se met en attente 
 13       - dans une autre fenêtre, faire
 14       cat > /tmp/mafifo
 15       puis taper quelques lignes de texte.

 16     */

 17     #include <sys/time.h>
 18     #include <sys/types.h>
 19     #include <sys/stat.h>
 20     #include <unistd.h>
 21     #include <stdio.h>
 22     #include <stdlib.h>
 23     #include <fcntl.h>

 24     #define TAILLE_TAMPON 100
 25     #define DELAI 500               /* en millisecondes */

 26     extern int AttenteDonnees(int, int);

 27     int main(int argc, char *argv[])
 28     {
 29             int fd;
 30             char symbole[] = "-\\|-";
 31             int n = 0;
 32             int numero_ligne = 1;

 33             if (argc != 2) {
 34                     fprintf(stderr, "Usage: %s fifo\n", argv[0]);
 35                     exit(EXIT_FAILURE);
 36             }
 37             printf("> Ouverture fifo %s ...\n", argv[1]);
 38             fd = open(argv[1], O_RDONLY);
 39             if (fd == -1) {
 40                     fprintf(stderr, "Ouverture refusée\n");
 41                     exit(EXIT_FAILURE);
 42             };
 43             printf("Ok\n");

 44             for (;;) {
 45                     int r;
 46                     r = AttenteDonnees(fd, DELAI);
 47                     if (r == 0) {
 48                             /* délai dépassé, on affiche un caractère suivi par backspace */
 49                             printf("%c\b", symbole[n++ % 4]);
 50                             fflush(stdout);
 51                     } else {
 52                             /* on a reçu quelque chose */
 53                             char buffer[TAILLE_TAMPON];
 54                             int nb;
 55                             nb = read(fd, buffer, TAILLE_TAMPON - 1);
 56                             if (nb <= 0)
 57                                     break;
 58                             buffer[nb] = '\0';
 59                             printf("%4d %s", numero_ligne++, buffer);
 60                     }
 61             }
 62             close(fd);
 63             exit(EXIT_SUCCESS);
 64     }
 


Page suivante Page précédente Table des matières