Scroll to navigation

SELECT(2) Manuel du programmeur Linux SELECT(2)

NOM

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

SYNOPSIS

#include <sys/select.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);
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 (consulter feature_test_macros(7)) :

pselect(): _POSIX_C_SOURCE >= 200112L

DESCRIPTION

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.

select() ne peut surveiller que des numéros de descripteurs de fichier inférieurs à FD_SETSIZE ; poll(2) et epoll(7) n'ont pas cette limitation. Voir les BOGUES.

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.

Notez bien : lors du renvoi, chacun des ensembles de descripteurs de fichier est modifié pour indiquer les descripteurs actuellement « prêts ». Ainsi, si on utilise select() dans une boucle, alors les ensembles doivent être réinitialisés avant chaque appel. L'implémentation des paramètres fd_set en tant que paramètres valeur-résultat est une erreur de conception évitée dans poll(2) et epoll(7).

Le contenu d'un ensemble de descripteurs de fichier peut être manipulé en utilisant les macros suivantes :

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.
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.
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.
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 :

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.
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.
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.
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).
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() a le type suivant :


struct timespec {

time_t tv_sec; /* secondes */
long tv_nsec; /* nanosecondes */ };

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

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
Un signal a été intercepté ; consultez signal(7).
nfds est négatif ou dépasse la limite de ressource RLIMIT_NOFILE (voir getrlimit(2)).
La valeur contenue dans timeout n'est pas valable.
Incapacité d'allouer de la mémoire pour des tables internes.

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, 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.

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 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.)

Émuler usleep(3)

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.

Dans les versions de la glibc 2.1 à 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).

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 version 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)
{

fd_set rfds;
struct timeval tv;
int retval;
/* 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), epoll(7), time(7)

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

COLOPHON

Cette page fait partie de la publication 5.10 du projet man-pages Linux. Une description du projet et des instructions pour signaler des anomalies et la dernière version de cette page peuvent être trouvées à l'adresse https://www.kernel.org/doc/man-pages/.

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 à debian-l10n-french@lists.debian.org.

1 novembre 2020 Linux