select
Section: System Calls (2)
Updated: 5 février 2023
Index
Return to Main Contents
NOM
select, pselect, FD_CLR, FD_ISSET, FD_SET, FD_ZERO, fd_set - Multiplexage
d'entrées-sorties synchrones
BIBLIOTHÈQUE
Bibliothèque C standard (libc, -lc)
SYNOPSIS
#include <sys/select.h>
typedef /* ... */ fd_set;
int select(int nfds, fd_set *_Nullable restrict readfds,
fd_set *_Nullable restrict writefds,
fd_set *_Nullable restrict exceptfds,
struct timeval *_Nullable restrict 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);
int pselect(int nfds, fd_set *_Nullable restrict readfds,
fd_set *_Nullable restrict writefds,
fd_set *_Nullable restrict exceptfds,
const struct timespec *_Nullable restrict timeout,
const sigset_t *_Nullable restrict sigmask);
Exigences de macros de test de fonctionnalités pour la glibc (consulter
feature_test_macros(7)) :
pselect() :
_POSIX_C_SOURCE >= 200112L
DESCRIPTION
Attention : select() ne peut surveiller que des descripteurs de
fichier dont le numéro est inférieur à FD_SETSIZE (1024) --- une limite
excessivement basse pour beaucoup d'applications modernes --- et cette
limite ne changera pas. Toutes les applications modernes devraient utiliser
à la place poll(2) ou epoll(7) qui ne souffrent pas de cette
limitation.
select() permet à 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 (par exemple une
entrée est possible). Un descripteur de fichier est considéré comme prêt
s'il est possible d'effectuer l'opération correspondante (par exemple
read(2) ou un write(2) assez petit) sans bloquer.
fd_set
Un type de structure qui peut représenter un ensemble de descripteurs de
fichier. Selon POSIX, le nombre maximal de descripteurs de fichier dans une
structure fd_set est la valeur de la macro FD_SETSIZE.
Ensembles de descripteurs de fichier
Les paramètres principaux de select() sont trois « ensembles » de
descripteurs de fichier (déclarés avec le type fd_set), qui permettent à
l'appelant d'attendre trois classes d'événements sur l'ensemble de
descripteurs de fichier indiqué. Chacun des paramètres de fd_set peut
être indiqué comme NULL si aucun descripteur de fichier ne doit être
surveillé pour la classe d'événements correspondante.
Important : lors de la sortie, chacun des ensembles de descripteurs de
fichier est modifié sur place pour indiquer quels descripteurs de fichier
sont actuellement « prêts ». Par conséquent, si on utilise select() dans
une boucle, les ensembles doivent être réinitialisés avant chaque appel.
Le contenu d'un ensemble de descripteurs de fichier peut être manipulé en
utilisant les macros suivantes :
- FD_ZERO()
-
Cette macro efface (supprime tous les descripteurs de fichier) set. Elle
doit être utilisée en tant que première étape de l'initialisation d'un
ensemble de descripteurs de fichier.
- FD_SET()
-
Cette macro ajoute le descripteur de fichier fd à set. L'ajout d'un
descripteur de fichier déjà présent dans l'ensemble est sans effet et ne
produit pas d'erreur.
- FD_CLR()
-
Cette macro supprime le descripteur de fichier fd de set. La
suppression d'un descripteur de fichier non présent dans l'ensemble est sans
effet et ne produit pas d'erreur.
- FD_ISSET()
-
select() modifie le contenu des ensembles en fonction des règles décrites
ci-dessous. Après un appel à select(), la macro FD_ISSET() peut être
utilisée pour tester si un descripteur de fichier est présent dans un
ensemble. FD_ISSET() ne renvoie pas zéro si le descripteur de fichier
fd est présent dans set, sinon il le renvoie.
Arguments
Les paramètres de select() sont les suivants :
- readfds
-
Les descripteurs de fichier de cet ensemble sont surveillés pour voir s'ils
sont prêts en lecture. Un descripteur de fichier est prêt si une opération
de lecture ne bloquera pas ; en particulier, le descripteur de fichier est
prêt sur une fin-de-fichier.
-
Après que select() ait renvoyé, readfds sera vidé de tous les
descripteurs de fichier sauf ceux prêts en lecture.
- writefds
-
Les descripteurs de fichier de cet ensemble sont surveillés pour voir s'ils
sont prêts en écriture. Un descripteur de fichier est prêt si une opération
d'écriture ne bloquera pas. Cependant, même si un descripteur de fichier est
indiqué comme inscriptible, une écriture abondante peut toujours bloquer.
-
Après que select() ait renvoyé, writefds() sera vidé de tous les
descripteurs de fichier, sauf ceux prêts en écriture.
- exceptfds
-
Les descripteurs de fichier de cet ensemble sont surveillés en cas de
« conditions exceptionnelles ». Pour des exemples de conditions
exceptionnelles, voir le point sur POLLPRI dans poll(2).
-
Après que select() ait renvoyé, exceptfds() sera vidé de tous les
descripteurs de fichier, sauf ceux où s'est produite une condition
exceptionnelle.
- nfds
-
Ce paramètre doit être positionné sur le numéro du plus grand descripteur
des trois ensembles, plus 1. Les descripteurs de fichier indiqués dans
chaque ensemble sont vérifiés dans cette limite (mais voir BOGUES).
- timeout
-
L'argument timeout est une structure timeval (décrite ci-dessous) qui
précise la durée de l'intervalle pendant lequel select() restera bloqué
dans l'attente d'un descripteur de fichier disponible. L'appel restera
bloqué jusqu'à :
-
- •
-
un descripteur de fichier devient prêt ;
- •
-
l’appel est interrompu par un gestionnaire de signal ;
- •
-
le délai expire.
-
Notez que l'intervalle timeout est arrondi selon la granularité de
l'horloge du système, et un retard d'ordonnancement du noyau peut entraîner
un léger dépassement de la durée de blocage.
-
Si les deux champs de la structure timeval valent zéro, select()
renvoie immédiatement (c'est utile pour la scrutation).
-
Si timeout est indiqué comme étant NULL, select() restera bloqué dans
l'attente d'un descripteur de fichier disponible :
pselect()
L'appel système pselect() permet à une application d'attendre de manière
sécurisée un signal ou qu'un descripteur de fichier soit prêt.
select() et pselect() ont un comportement identique, avec trois
différences :
- •
-
La fonction select() utilise un délai exprimé avec une struct timeval
(en secondes et microsecondes), alors que pselect() utilise une struct timespec (en secondes et nanosecondes).
- •
-
La fonction select() peut modifier le paramètre timeout pour indiquer
le temps restant. La fonction pselect() ne change pas ce paramètre.
- •
-
La fonction select() n'a pas de paramètre sigmask et se comporte comme
pselect() avec une valeur NULL pour sigmask
sigmask est un pointeur sur un masque de signaux (consultez
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 (si sigmask est NULL, le masque de signaux n'est pas modifié
pendant l'appel pselect()).
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;
pthread_sigmask(SIG_SETMASK, &sigmask, &origmask);
ready = select(nfds, &readfds, &writefds, &exceptfds, timeout);
pthread_sigmask(SIG_SETMASK, &origmask, NULL);
La raison de la présence de 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 renvoie. 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 les signaux d'abord,
traiter les signaux déjà reçus, puis invoquer pselect() avec le
sigmask désiré, en évitant la situation de concurrence.)
Délai
Le paramètre timeout de select() est une structure du type suivant :
struct timeval {
time_t tv_sec; /* secondes */
suseconds_t tv_usec; /* microsecondes */
};
Le paramètre correspondant à pselect() est une structure timespec(3).
Sous Linux, la fonction select() modifie timeout pour indiquer le
temps non endormi ; la plupart des autres implémentations ne le font pas
(POSIX.1 autorise les deux comportements). Cela 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 pour plusieurs select()s
dans une boucle 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 renvoyés (c'est-à-dire
le nombre total de bits définis dans readfds, writefds et
exceptfds) qui peut être nul si le délai d’expiration a été atteint avant
qu'un descripteur de fichier ne soit prêt.
En cas d'erreur, la valeur de retour est -1 et errno est définie pour
préciser l'erreur ; les ensembles de descripteurs de fichiers ne sont pas
modifiés et timeout devient indéfini.
ERREURS
- EBADF
-
Un descripteur de fichier non valable était dans l'un des ensembles
(peut-être un descripteur déjà fermé ou sur lequel une erreur s'est
produite).Cependant, consultez BOGUES
- EINTR
-
Un signal a été intercepté ; consultez signal(7).
- EINVAL
-
nfds est négatif ou dépasse la limite de ressource RLIMIT_NOFILE (voir
getrlimit(2)).
- EINVAL
-
La valeur contenue dans timeout n'est pas valable.
- ENOMEM
-
Incapacité d'allouer de la mémoire pour des tables internes.
VERSIONS
pselect() a été ajouté dans Linux 2.6.16. Précédemment, pselect()
était émulé dans la glibc (mais voir la section BOGUES).
STANDARDS
select() est conforme à POSIX.1-2001, POSIX.1-2008 et 4.4BSD (la fonction
select() est apparue dans 4.2BSD). Généralement portable depuis ou vers
des systèmes non BSD gérant des clones de la couche sockets BSD (y compris
les variantes de System V). Néanmoins, sachez que les variantes de System V
définissent la variable timeout avant le retour alors que les variantes
BSD ne le font pas.
pselect() est défini dans POSIX.1g ainsi que dans POSIX.1-2001 et
POSIX.1-2008.
fd_set est défini dans POSIX.1-2001 et suivants.
NOTES
L'en-tête suivant fournit aussi le type fd_set : <sys/time.h>.
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 valable.
Les opérations select() et pselect() ne sont pas concernées par
l'attribut O_NONBLOCK.
Sur d'autres systèmes UNIX, select() peut échouer avec l'erreur EAGAIN
si le système ne parvient pas à allouer des ressources internes du noyau
contrairement à l'erreur ENOMEM de Linux. POSIX spécifie cette erreur
pour poll(2) mais pas pour select(2). Des programmes portables peuvent
souhaiter vérifier EAGAIN et la boucle comme avec EINTR.
L'astuce du « self-pipe »
Sur les systèmes sans pselect, une gestion plus sûre (et plus portable)
des signaux peut être obtenue en utilisant l'astuce du « self-pipe » : un
gestionnaire de signal écrit un octet dans un tube dont select() dans le
programme principal surveille l'autre extrémité. (Pour éviter la possibilité
de blocage lors de l'écriture dans un tube pouvant être plein ou de la
lecture dans un tube pouvant être vide, des entrées et sorties non
bloquantes sont utilisées pour la lecture et l'écriture dans le tube.)
Avant l'arrivée de usleep(3), certaines applications appelaient
select() avec trois ensembles de descripteurs vides, nfds nul et un
délai timeout non NULL, afin d'endormir, de manière portable, le
processus avec une précision plus fine que la seconde.
Correspondance entre les notifications de select() et de poll()
Dans les sources du noyau Linux, nous trouvons les définitions suivantes qui
montrent la correspondance entre les notifications de lisibilité,
d'inscriptibilité et de condition exceptionnelle de select() et les
notifications d'événements fournies par poll(2) et epoll(7) :
#define POLLIN_SET (EPOLLRDNORM | EPOLLRDBAND | EPOLLIN |
EPOLLHUP | EPOLLERR)
/* Prêt en lecture */
#define POLLOUT_SET (EPOLLWRBAND | EPOLLWRNORM | EPOLLOUT |
EPOLLERR)
/* Prêt en écriture */
#define POLLEX_SET (EPOLLPRI)
/* Condition exceptionnelle */
Programmes multithreadés
Si un descripteur de fichier surveillé par select() est fermé dans un
autre thread, le résultat est indéterminé. Sur certains systèmes UNIX,
select() débloque et termine, avec une indication que le descripteur de
fichier est prêt (une opération entrée/sortie ultérieure risque d'échouer
avec une erreur, sauf si le descripteur de fichier a été réouvert entre le
moment où select() termine et l'exécution des opérations
entrée/sortie). Sur Linux (et d'autres systèmes), la fermeture du
descripteur de fichier dans un autre thread n'a aucun effet sur
select(). En résumé, toute application qui s'appuie sur un comportement
particulier dans ce scénario doit être considérée comme boguée.
différences entre bibliothèque C et noyau
Le noyau Linux autorise les ensembles de descripteurs de fichier de
n'importe quelle taille, en déterminant la taille des ensembles à vérifier à
partir de la valeur de nfds. Cependant, dans l'implémentation de la
glibc, le type fd_set a une taille fixe. Voir aussi les BOGUES.
L'interface pselect() décrite dans cette page est implémentée par la
glibc. L'appel système Linux sous-jacent est appelé pselect6(). Cet appel
système a un comportement quelque peu différent de la fonction d'enveloppe
de la glibc.
L'appel système pselect6() de Linux modifie son argument
timeout. Cependant, la fonction d'enveloppe 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 la
glibc ne modifie pas son paramètre timeout, ce qui est le comportement
prescrit par POSIX.1-2001.
Le dernier argument de l'appel système pselect6() n'est pas un pointeur
sigset_t *, mais une structure de la forme suivante :
struct {
const kernel_sigset_t *ss; /* Pointeur vers un ensemble de signaux */
size_t ss_len; /* Taille (en octets) de l'objet vers
lequel pointe 'ss' */
};
Cela permet à l'appel système d'obtenir à la fois le pointeur vers
l'ensemble de signaux et sa taille, tout en permettant à la plupart des
architectures de ne prendre en charge qu'un maximum de 6 arguments pour un
appel système. Voir sigprocmask(2) pour un point sur la différence entre
la vision du noyau et celle de la libc de l'ensemble de signaux.
Détails historiques sur la glibc
La glibc 2.0 fournissait une mauvaise version de pselect() qui n'avait
pas d'argument sigmask.
De la glibc 2.1 à la glibc 2.2.1, on peut définir _GNU_SOURCE afin
d'obtenir la déclaration de pselect() depuis <sys/select.h>.
BOGUES
POSIX autorise une implémentation à définir une limite supérieure indiquée à
l'aide de la constante FD_SETSIZE, dans l'intervalle de descripteurs de
fichier qui peuvent être indiqués dans un ensemble de descripteurs de
fichier. Le noyau Linux n'impose pas de limite fixe mais l'implémentation de
la glibc fait que fd_set est un type de taille fixe, où FD_SETSIZE est
défini à 1024 et où les macros FD_*() agissent en fonction de cette
limite. Pour surveiller des descripteurs de fichier supérieurs à 1023,
utilisez plutôt poll(2) ou epoll(7).
L'implémentation des paramètres de fd_set en tant qu'arguments
valeur-résultat est une erreur de conception qui est évitée dans poll(2)
et epoll(7).
Selon POSIX, select() devrait vérifier tous les descripteurs de fichier
des trois ensembles jusqu'à nfds-1. Cependant, l'implémentation actuelle
ignore tout descripteur de fichier dans ces ensembles supérieur au numéro le
plus élevé de descripteur de fichier que le processus a ouvert. Selon POSIX,
un tel descripteur de fichier indiqué dans l'un des ensembles devrait
provoquer une erreur EBADF.
À partir de la glibc 2.1, la glibc fournissait une émulation de pselect()
implémentée avec sigprocmask(2) et select(). Cette implémentation
était vulnérable à la condition de concurrence que pselect() était conçu
pour éviter. Les versions récentes de la glibc utilisent l'appel système
pselect() (sans risque de concurrence) si le noyau le fournit.
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 dans lesquelles le descripteur
de fichier est faussement signalé comme 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 (code d'erreur EINTR). Cela est
interdit par POSIX.1. 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.
EXEMPLES
#include <stdio.h>
#include <stdlib.h>
#include <sys/select.h>
int
main(void)
{
int retval;
fd_set rfds;
struct timeval tv;
/* Surveiller stdin (fd 0) en attente d'entrées */
FD_ZERO(&rfds);
FD_SET(0, &rfds);
/* Attendre jusqu'à 5 secondes. */
tv.tv_sec = 5;
tv.tv_usec = 0;
retval = select(1, &rfds, NULL, NULL, &tv);
/* Ne pas s'appuyer sur la valeur de tv 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
accept(2), connect(2), poll(2), read(2), recv(2),
restart_syscall(2), send(2), sigprocmask(2), write(2),
timespec(3), epoll(7), time(7)
Pour un tutoriel avec des exemples, consultez select_tut(2).
TRADUCTION
La traduction française de cette page de manuel a été créée par
Christophe Blaess <https://www.blaess.fr/christophe/>,
Stéphan Rafin <stephan.rafin@laposte.net>,
Thierry Vignaud <tvignaud@mandriva.com>,
François Micaux,
Alain Portal <aportal@univ-montp2.fr>,
Jean-Philippe Guérard <fevrier@tigreraye.org>,
Jean-Luc Coulon (f5ibh) <jean-luc.coulon@wanadoo.fr>,
Julien Cristau <jcristau@debian.org>,
Thomas Huriaux <thomas.huriaux@gmail.com>,
Nicolas François <nicolas.francois@centraliens.net>,
Florentin Duneau <fduneau@gmail.com>,
Simon Paillard <simon.paillard@resel.enst-bretagne.fr>,
Denis Barbier <barbier@debian.org>,
David Prévot <david@tilapin.org>,
Cédric Boutillier <cedric.boutillier@gmail.com>,
Frédéric Hantrais <fhantrais@gmail.com>,
Jean-Philippe MENGUAL <jpmengual@debian.org>
et
Jean-Pierre Giraud <jean-pierregiraud@neuf.fr>
Cette traduction est une documentation libre ; veuillez vous reporter à la
GNU General Public License version 3
concernant les conditions de copie et
de distribution. Il n'y a aucune RESPONSABILITÉ LÉGALE.
Si vous découvrez un bogue dans la traduction de cette page de manuel,
veuillez envoyer un message à
Index
- NOM
-
- BIBLIOTHÈQUE
-
- SYNOPSIS
-
- DESCRIPTION
-
- fd_set
-
- Ensembles de descripteurs de fichier
-
- Arguments
-
- pselect()
-
- Délai
-
- VALEUR RENVOYÉE
-
- ERREURS
-
- VERSIONS
-
- STANDARDS
-
- NOTES
-
- L'astuce du « self-pipe »
-
- Émuler usleep(3)
-
- Correspondance entre les notifications de select() et de poll()
-
- Programmes multithreadés
-
- différences entre bibliothèque C et noyau
-
- Détails historiques sur la glibc
-
- BOGUES
-
- EXEMPLES
-
- VOIR AUSSI
-
- TRADUCTION
-
This document was created by
man2html,
using the manual pages.
Time: 04:02:19 GMT, May 18, 2024