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
select() et pselect() permettent à un programme de surveiller plusieurs
descripteurs de fichier, en attendant qu’au moins l’un de ces
descripteurs soit « prêt » pour une certaine classe d’opérations
d’entrée-sortie. Un descripteur de fichier est considéré comme prêt
s’il est possible d’effectuer l’opération correspondante (par exemple
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 qui ont
changé de statut. Chacun des trois ensemble de descripteurs peut être
NULL si aucun descripteur de fichier ne doit être surveillé pour cette
classe 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 un descripteur dans un ensemble. FD_ISSET() vérifie si un
descripteur est contenu dans un ensemble, principalement utile après le
retour de select().
nfds est le numéro du plus grand descripteur des 3 ensembles, plus 1.
timeout est une limite supérieure au temps passé dans select() avant
son retour. Si les deux champs de la structure timeval sont nuls alors
select() retourne immédiatement. (Ce qui sert pour des surveillances en
polling). Si le timeout est NULL (aucune limite), 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.
Mise à part la différence de précision de l’argument timeout, l’appel
pselect() suivant :
ready = pselect(nfds, &readfds, &writefds, &exceptfds,
timeout, &sigmask);
est équivalent à exécuter de façon atomique les appels suivants :
sigset_t origmask;
sigprocmask(SIG_SETMASK, &sigmask, &origmask);
ready = select(nfds, &readfds, &writefds, &exceptfds, timeout);
sigprocmask(SIG_SETMASK, &origmask, NULL);
L’idée derrière pselect() est que pour l’attente d’un événement, que ce
soit un signal ou une condition sur un descripteur, 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 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 struct 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 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 invalide était dans l’un des
ensembles. (Peut-être un descripteur 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 est
invalide.
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
du System V fixent une variable de timeout avant le retour alors que
les variantes BSD ne le font pas.
pselect() 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-dessus), et que la structure soit définie dans <sys/time.h>. La
situation avec POSIX.1-2001 est
struct timeval {
time_t tv_sec; /* secondes */
suseconds_t tv_usec; /* microsecondes */
};
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’en-tê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. Depuis la glibc
2.2.2, les exigences sont celles indiquées dans le SYNOPSIS.
Notes sur Linux
L’appel système pselect() de Linux modifie son argument timeout.
Cependant, la fonction d’enrobage de la glibc cache ce comportement en
utilisant une variable locale pour l’argument timeout qui est passé à
l’appel système. Par conséquent, la fonction pselect() de glibc ne
modifie pas son argument timeout, ce qui est le comportement prescrit
par POSIX.1-2001.
BOGUES
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()
implémentée avec sigprocmask(2) et select(). Cette implémentation est
vulnérable à la condition de concurrence que pselect() est conçu pour
éviter. Sur les systèmes sans pselect, une gestion plus sûre (et plus
portable) des signaux peut être obtenue en utilisant un tube (un
gestionnaire de signal écrit un octet dans un tube dont select() dans
le programme principal surveille l’autre extrémité).
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, après
vérification, ont une mauvaise somme de contrôle et sont rejetées. Cela
peut également arriver dans d’autres circonstances. 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 (code d’erreur EINTR). Ceci
est interdit par POSIX.1-2001. L’appel système pselect() de Linux se
comporte de la même façon, mais la glibc cache cette particularité en
copiant timeout vers 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);
/* Attends jusqu’à 5 secondes. */
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 alors vrai. */
else
printf("Aucune donnée durant les cinq 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)
COLOPHON
Cette page fait partie de la publication 3.23 du projet man-pages
Linux. Une description du projet et des instructions pour signaler des
anomalies peuvent être trouvées à l’adresse
http://www.kernel.org/doc/man-pages/.
TRADUCTION
Cette page de manuel a été traduite et mise à jour par Christophe
Blaess <http://www.blaess.fr/christophe/> entre 1996 et 2003, puis par
Alain Portal <aportal AT univ-montp2 DOT fr> jusqu’en 2006, et mise à
disposition sur http://manpagesfr.free.fr/.
Les mises à jour et corrections de la version présente dans Debian sont
directement gérées par Julien Cristau <jcristau@debian.org> et l’équipe
francophone de traduction de Debian.
Veuillez signaler toute erreur de traduction en écrivant à
<debian-l10n-french@lists.debian.org> ou par un rapport de bogue sur le
paquet manpages-fr.
Vous pouvez toujours avoir accès à la version anglaise de ce document
en utilisant la commande « man -L C <section> <page_de_man> ».