SELECT

Section : Manuel du programmeur Linux (2)
Mise à jour de la version anglaise : 26 juillet 2007
Index Menu principal  

NOM

select, pselect, FD_CLR, FD_ISSET, FD_SET, FD_ZERO - Multiplexage d'entrées-sorties synchrones  

SYNOPSIS

/* D'après POSIX.1-2001 */

#include <sys/select.h> /* D'après les standards précédents */
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h> int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout); void FD_CLR(int fd, fd_set *set);
int FD_ISSET(int fd, fd_set *set);
void FD_SET(int fd, fd_set *set);
void FD_ZERO(fd_set *set); #include <sys/select.h> int pselect(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, const struct timespec *timeout, const sigset_t *sigmask);

Exigences de macros de test de fonctionnalités pour la glibc (voir feature_test_macros(7)) :

pselect() : _POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 600  

DESCRIPTION

Les fonctions select() et pselect() permettent à un programme de surveiller plusieurs descripteurs de fichier, attendant qu'au moins l'un des descripteurs de fichier devienne « prêt » pour certaines classes d'opérations d'entrées-sorties (par exemple, entrée possible). Un descripteur de fichier est considéré comme prêt s'il est possible d'effectuer l'opération d'entrées-sorties correspondante (par exemple, un read(2)) sans bloquer.

select() et pselect() ont un comportement identique, avec trois différences :

(i)
La fonction select() utilise un délai exprimé avec une struct timeval (secondes et microsecondes), alors que pselect() utilise une struct timespec (secondes et nanosecondes).
(ii)
La fonction select() peut modifier le paramètre timeout pour indiquer le temps restant. La fonction pselect() ne change pas ce paramètre.
(iii)
La fonction select() n'a pas de paramètre sigmask et se comporte comme pselect() avec une valeur NULL pour sigmask

Il y a trois ensembles indépendants de descripteurs surveillés simultanément. Ceux de l'ensemble readfds seront surveillés pour vérifier si des caractères deviennent disponibles en lecture. Plus précisément, on vérifie si un appel système de lecture ne bloquera pas ; en particulier un descripteur en fin de fichier sera considéré comme prêt. Les descripteurs de l'ensemble writefds seront surveillés pour vérifier si une écriture ne bloquera pas. Ceux de exceptfds seront surveillés pour l'occurrence de conditions exceptionnelles. En sortie, les ensembles sont modifiés pour indiquer les descripteurs de fichier qui ont changé d'état. Chacun des trois ensembles de descripteurs de fichier peut être spécifié comme NULL si aucun descripteur de fichier n'est surveillé pour la classe correspondante d'événements.

Quatre macros sont disponibles pour la manipulation des ensembles FD_ZERO() efface un ensemble. FD_SET() et FD_CLR() ajoutent et suppriment, respectivement, un descripteur de fichier dans un ensemble. FD_ISSET() vérifie si un descripteur de fichier est contenu dans un ensemble, principalement utile après le retour de select().

nfds est le numéro du plus grand descripteur de fichier des 3 ensembles, plus 1.

timeout est une limite supérieure au temps passé dans select() avant son retour. Elle peut être nulle, ce qui conduit select() à revenir immédiatement. (Ce qui sert pour des surveillances en polling). Si le timeout est NULL (aucun), select() peut bloquer indéfiniment.

sigmask est un pointeur sur un masque de signaux (voir sigprocmask(2)). S'il n'est pas NULL, alors pselect() remplace d'abord le masque de signaux en cours par celui indiqué dans sigmask, puis invoque la fonction « select », et enfin restaure le masque de signaux à nouveau.

Outre la différence de précision de l'argument timeout, l'appel pselect() suivant


    ready = pselect(nfds, &readfds, &writefds, &exceptfds, 
                    timeout, &sigmask);

est équivalent à l'exécution atomique des appels suivants :

    sigset_t origmask;

    sigprocmask(SIG_SETMASK, &sigmask, &origmask);
    ready = select(nfds, &readfds, &writefds, &exceptfds, timeout);
    sigprocmask(SIG_SETMASK, &origmask, NULL);

La raison pour laquelle pselect() est nécessaire est que si l'on veut attendre soit un signal, soit qu'un descripteur de fichier soit prêt, alors un test atomique est nécessaire pour éviter les situations de concurrence. (Supposons que le gestionnaire de signaux active un drapeau global et revienne. Alors un test de ce drapeau, suivi d'un appel select() peut bloquer indéfiniment si le signal arrive juste après le test mais avant l'appel. À l'inverse, pselect() permet de bloquer le signal d'abord, traiter les signaux déjà reçus, puis invoquer pselect() avec le sigmask, désiré, en évitant la situation de blocage.)  

Délai maximal

Les structures temporelles concernées sont définies dans <sys/time.h> comme ceci :
struct timeval {
    long    tv_sec;         /* secondes      */
    long    tv_usec;        /* microsecondes */
};

et

struct timespec {
    long    tv_sec;         /* secondes     */
    long    tv_nsec;        /* nanosecondes */
};

(Toutefois, voir plus loin les versions POSIX.1-2001.)

Certaines applications appellent select() avec les trois ensembles de descripteurs vides, nfds nul, et un délai timeout non nul, afin d'endormir, de manière portable, le processus avec une précision plus fine que la seconde.

Sous Linux, la fonction select() modifie timeout pour indiquer le temps restant mais la plupart des autres implémentations ne le font pas (POSIX.1-2001 autorise les deux comportements). Ceci pose des problèmes à la fois pour porter sur d'autres systèmes du code développé sous Linux qui utilise cette valeur de timeout modifiée, et pour porter sous Linux du code qui réutilise plusieurs fois la structure timeval sans la réinitialiser. La meilleure attitude à adopter est de considérer timeout comme indéfini après le retour de select().  

VALEUR RENVOYÉE

En cas de réussite select() et pselect() renvoient le nombre de descripteurs de fichier dans les trois ensembles de descripteurs retournés (c'est-à-dire, le nombre total de bits à 1 dans readfds, writefds, exceptfds) qui peut être nul si le délai de timeout a expiré avant que quoi que ce soit d'intéressant ne se produise. Ils retournent -1 s'ils échouent, auquel cas errno contient le code d'erreur ; les ensembles et timeout ne sont plus définis, ne vous fiez plus à leur contenu après une erreur.  

ERREURS

EBADF
Un descripteur de fichier (dans l'un des ensembles) est invalide. (Peut-être un descripteur de fichier qui était déjà fermé ou sur lequel une erreur s'est produite.
EINTR
Un signal a été intercepté ; voir signal(7).
EINVAL
nfds est négatif ou la valeur contenue dans timeout n'est pas valide.
ENOMEM
Pas assez de mémoire pour le noyau.
 

VERSIONS

pselect() a été ajouté à Linux dans le noyau 2.6.16.

Précédemment, pselect() était émulé dans la glibc (mais voir la section BOGUES).  

CONFORMITÉ

select() est conforme à POSIX.1-2001 et BSD 4.4 (la fonction select() est apparue dans BSD 4.2). Généralement portable depuis ou vers des systèmes non-BSD supportant des clones de la couche sockets BSD (y compris les variantes du System V). Néanmoins, sachez que les variantes de System V fixent une variable de timeout avant le retour alors que les variantes BSD ne le font pas.

select() est défini dans POSIX.1g et dans POSIX.1-2001.  

NOTES

Un ensemble fd_set est un tampon de taille fixe. Exécuter FD_CLR() ou FD_SET() avec fd négatif ou supérieur ou égal à FD_SETSIZE résultera en un comportement indéfini. Plus encore, POSIX demande que fd soit un descripteur de fichier valide.

En ce qui concerne les types impliqués, la situation classique est que les deux champs de la structure timeval soient de type long (comme ci-dessous), et que la structure soit définie dans <sys/time.h>. La situation avec POSIX 1003.1-2001 est

struct timeval {
    time_t         tv_sec;     /* seconds */
    suseconds_t    tv_usec;    /* microseconds */
};

avec la structure définie dans <sys/select.h> et les types de données time_t et suseconds_t définis dans <sys/types.h>.

Concernant les prototypes, on demande classiquement l'inclusion de <time.h> pour select(). Avec POSIX.1-2001, on préfère inclure <sys/select.h> pour select() et pselect(). Les bibliothèques libc4 et libc5 n'avaient pas d'entête <sys/select.h>, mais avec les glibc 2.0 et suivantes le fichier existe. Pour la glibc 2.0, le prototype de pselect() est toujours erroné. Avec la glibc 2.1-2.2.1 le prototype de pselect() est fourni si la constante _GNU_SOURCE est définie avant l'inclusion. Avec glibc 2.2.2-2.2.4, il faut que la constante _XOPEN_SOURCE soit définie avec une valeur supérieure ou égale à 600. Quoiqu'il en soit, depuis POSIX.1-2001, le bon prototype devrait être défini par défaut.  

Notes Linux

L'appel système pselect() de Linux modifie son argument timeout. Toutefois, la fonction enveloppe de la glibc cache ce comportement en utilisant une variable locale pour l'argument timeout qui est passé à l'appel système. Ainsi, la fonction pselect() de la glibc ne modifie pas son argument timeout, ce qui est le comportement prescrit par POSIX.1-2001.  

BOGUES

La glibc 2.0 fournissait une version de pselect() qui n'avait pas d'argument sigmask.

Depuis la version 2.1, la glibc fournit une émulation de pselect() qui est implémentée en utilisant sigprocmask(2) et select(). Cette implémentation reste vulnérable aux situations de concurrence pour lesquelles pselect() a été conçue afin de les prévenir. Sur les systèmes où pselect() est absent, une capture de signal fiable (et plus portable) peut être réalisée en utilisant l'astuce auto-tube (où un gestionnaire de signaux écrit un octet dans un tube dont l'autre fin est surveillée par select() dans la boucle principale).

Sous Linux, select() peut signaler un descripteur de fichier socket comme « prêt à lire » alors qu'une lecture suivante bloque. Cela peut, par exemple, survenir lorsque des données sont arrivées mais qui, après vérification, ont une mauvaise somme de contrôle et sont rejetées. Cela peut également arriver dans d'autres circonstances où le descripteur de fichier est faussement rapporté comme étant prêt. Aussi, il est plus sûr d'utiliser O_NONBLOCK sur des sockets qui ne devraient pas bloquer.

Sous Linux, select() modifie également timeout si l'appel est interrompu par un gestionnaire de signaux (c'est-à-dire, l'erreur EINTR a été retournée). Ceci n'est pas permi par POSIX.1-2001. L'appel système Linux pselect() a le même comportement, mais l'enveloppe de la glibc cache ce comportement timeout dans une variable locale et en passant cette variable à l'appel système.  

EXEMPLE

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

int
main(void)
{
    fd_set rfds;
    struct timeval tv;
    int retval;

    /* Surveiller stdin (fd 0) en attente d'entrées */
    FD_ZERO(&rfds);
    FD_SET(0, &rfds);

    /* Pendant 5 secondes maxi */
    tv.tv_sec = 5;
    tv.tv_usec = 0;

    retval = select(1, &rfds, NULL, NULL, &tv);
    /* Considérer tv comme indéfini maintenant ! */

    if (retval == -1)
        perror("select()");
    else if (retval)
        printf("Des données sont disponibles maintenant\n");
        /* FD_ISSET(0, &rfds) est vrai */
    else
        printf("Aucune données durant les 5 secondes\n");

    exit(EXIT_SUCCESS);
}
 

VOIR AUSSI

Pour un tutoriel avec des exemples, voir select_tut(2).

D'autres pages ayant un vague rapport : accept(2), connect(2), poll(2), read(2), recv(2), send(2), sigprocmask(2), write(2), epoll(7), time(7)  

TRADUCTION

Ce document est une traduction réalisée par Christophe Blaess <http://www.blaess.fr/christophe/> le 13 octobre 1996 et révisée le 17 juillet 2008.

L'équipe de traduction a fait le maximum pour réaliser une adaptation française de qualité. La version anglaise la plus à jour de ce document est toujours consultable via la commande : « LANG=C man 2 select ». N'hésitez pas à signaler à l'auteur ou au traducteur, selon le cas, toute erreur dans cette page de manuel.

 

Index

NOM
SYNOPSIS
DESCRIPTION
Délai maximal
VALEUR RENVOYÉE
ERREURS
VERSIONS
CONFORMITÉ
NOTES
Notes Linux
BOGUES
EXEMPLE
VOIR AUSSI
TRADUCTION

Dernière mise à jour : 17 juillet 2008