NOM
epoll - Notifications d’événements d’entrées-sorties.
SYNOPSIS
#include <sys/epoll.h>
epoll est une variante de poll(2) que l’on peut déclencher par niveau
ou par changement d’état, et monte bien en charge pour un grand nombre
de descripteurs simultanés. Les appels système suivants sont fournis
pour créer et superviser une instance epoll :
* Une instance epoll créée par epoll_create(2), qui renvoie un
descripteur de fichier référençant l’instance epoll (la version plus
récente epoll_create1(2) étend les fonctionnalités de
epoll_create(2)).
* L’intérêt pour un descripteur de fichier est ensuite enregistré avec
epoll_ctl(2). L’ensemble de descripteurs de fichiers actuellement
enregistré pour une instance epoll est parfois appelé un ensemble
epoll.
* Enfin, l’attente est effectivement démarrée avec epoll_wait(2).
Détection de niveau et détection de transition
L’interface de distribution d’événements de epoll est capable de se
comporter en détection de niveau (Level Triggered - LT) ou en détection
de transition (Edge Triggered - ET). La différence entre ces deux
mécanismes est décrite ci-dessous. Supposons que le scénario suivant se
produise :
1. Le descripteur de fichier qui représente le côté lecture d’un tube
(rfd) est enregistré dans l’instance epoll.
2. Celui qui écrit dans le tube envoie 2 Ko de données.
3. Un appel à epoll_wait(2) est effectué et renvoie rfd comme
descripteur de fichier prêt.
4. Le lecteur du tube lit 1 Ko de données depuis rfd.
5. Un appel de epoll_wait(2) est effectué.
Si le descripteur rfd a été ajouté à l’ensemble epoll en utilisant
l’attribut EPOLLET (edge-triggered), l’appel epoll_wait(2), réalisé à
l’étape 5, va probablement bloquer bien qu’il y ait des données
toujours présentes dans les tampons d’entrée du fichier et le pair
distant attendra une réponse basée sur les données qu’il a déjà
envoyées. La raison en est que le mécanisme de distribution
d’événements Edge Triggered délivre les événements seulement lorsque
des événements surviennent sur le fichier supervisé. Ainsi, à l’étape
5, l’appelant peut attendre des données qui sont déjà présentes dans le
tampon d’entrée. Dans l’exemple ci-dessus, un événement sur rfd sera
déclenché à cause de l’écriture à l’étape 2, et l’événement est
consommé dans 3. Comme l’opération de lecture de l’étape 4 ne consomme
pas toutes les données du tampon, l’appel à epoll_wait(2) effectué à
l’étape 5 peut verrouiller indéfiniment.
Une application qui emploie l’attribut EPOLLET de la fonction epoll
devrait toujours utiliser des descripteurs non bloquants pour éviter
qu’une lecture ou une écriture ne bloque, par une famine, une tâche qui
gère plusieurs descripteurs de fichier. L’utilisation préconisée
d’epoll avec l’interface en détection de changements (EPOLLET) est la
suivante :
i avec des descripteurs non bloquants ; et
ii en attendant seulement après qu’un read(2) ou un write(2) a
renvoyé EAGAIN.
Au contraire, lorsqu’il est utilisé avec l’interface en détection de
niveau (par défaut si EPOLLET n’est pas spécifié), epoll est une
alternative plus rapide à poll(2), et peut être employé chaque fois que
ce dernier est utilisé, car il utilise la même sémantique.
Même dans un epoll de type Edge Triggered, plusieurs événements peuvent
être générés à la réception de nombreux blocs de données. L’appelant
peut, en spécifiant l’attribut EPOLLONESHOT, faire désactiver par epoll
le descripteur de fichier associé, après la réception d’un événement
avec epoll_wait(2). Lorsque l’attribut EPOLLONESHOT est spécifié, il
est de la responsabilité de l’appelant de réarmer le descripteur en
utilisant epoll_ctl(2) avec EPOLL_CTL_MOD.
Interfaces /proc
Les interfaces suivantes peuvent être utilisées pour limiter la
quantité de mémoire du noyau utilisée par epoll :
/proc/sys/fs/epoll/max_user_watches (depuis Linux 2.6.28)
Ceci définit une limite au nombre total de descripteurs de
fichiers qu’un utilisateur peut enregistrer au travers toutes
les instances epoll du système. La limite est imposée par
identifiant d’utilisateur réel. Chaque descripteur de fichier
enregistré coûte environ 90 octets sur un noyau 32 bits et
environ 160 octets sur un noyau 64 bits. Actuellement la valeur
par défaut pour max_user_watches est de 1/25 (4%) de la mémoire
basse disponible, divisé par le coût d’allocation en octets.
Exemple d’utilisation
Tandis que l’utilisation de epoll avec un déclenchement par niveau
correspond à la même sémantique que poll(2), le déclenchement par
changement d’état nécessite plus d’explication pour éviter les cas de
blocage. Dans cet exemple, le lecteur emploie une socket non bloquante
sur laquelle listen(2) a été appelée. La fonction do_use_fd() va
utiliser le nouveau descripteur de fichier, jusqu’à ce que EAGAIN soit
renvoyé par read(2) ou par write(2). Une application fonctionnant par
transition d’état devrait, après réception d’EAGAIN, enregistrer l’état
en cours, afin que l’appel suivant de do_use_fd() continue avec le
read(2) ou le write(2) où il s’est arrêté.
#define MAX_EVENTS 10
struct epoll_event ev, events[MAX_EVENTS];
int listen_sock, conn_sock, nfds, epollfd;
/* Set up listening socket, 'listen_sock' (socket(),
bind(), listen()) */
epollfd = epoll_create(10);
if (epollfd == -1) {
perror("epoll_create");
exit(EXIT_FAILURE);
}
ev.events = EPOLLIN;
ev.data.fd = listen_sock;
if (epoll_ctl(epollfd, EPOLL_CTL_ADD, listen_sock, &ev) == -1) {
perror("epoll_ctl: listen_sock");
exit(EXIT_FAILURE);
}
for (;;) {
nfds = epoll_wait(epollfd, events, MAX_EVENTS, -1);
if (nfds == -1) {
perror("epoll_pwait");
exit(EXIT_FAILURE);
}
for (n = 0; n < nfds; ++n) {
if (events[n].data.fd == listen_sock) {
conn_sock = accept(listen_sock,
(struct sockaddr *) &local, &addrlen);
if (conn_sock == -1) {
perror("accept");
exit(EXIT_FAILURE);
}
setnonblocking(conn_sock);
ev.events = EPOLLIN | EPOLLET;
ev.data.fd = conn_sock;
if (epoll_ctl(epollfd, EPOLL_CTL_ADD, conn_sock,
&ev) == -1) {
perror("epoll_ctl: conn_sock");
exit(EXIT_FAILURE);
}
} else {
do_use_fd(events[n].data.fd);
}
}
}
Lorsqu’on utilise une détection de changement d’états, pour des raisons
de performances, il est possible d’ajouter le descripteur de fichier
dans l’interface epoll (EPOLL_CTL_ADD) une fois, en spécifiant
(EPOLLIN|EPOLLOUT). Ceci évite de basculer sans cesse entre EPOLLIN et
EPOLLOUT lors des appels epoll_ctl(2) avec EPOLL_CTL_MOD.
Questions/Réponses
Q0 Quelle est la clé utilisée pour distinguer les descripteurs de
fichier enregistrés dans un ensemble epoll ?
A0 La clé est une combinaison du numéro du descripteur de fichier et
de la description du fichier ouvert (aussi connue comme « open file
handle », la représentation interne au noyau d’un fichier ouvert).
Q1 Que se passe-t-il si on enregistre deux fois le même descripteur de
fichier dans une instance epoll ?
A1 Vous aurez probablement un EEXIST. Cependant il est possible
d’ajouter un duplicat de descripteur (dup(2), dup2(2), fcntl(2)
F_DUPFD) sur la même instance epoll. Ceci peut être une technique
utile pour le filtrage d’événements, si les descripteurs duplicats
sont enregistré avec un masque d’événements events différent.
Q2 Deux instances epoll peuvent-elles attendre le même descripteur de
fichier ? Si oui, les événements seront-ils reportés sur les deux
descripteurs de fichier epoll en même temps ?
A2 Oui, et les événements seront rapportés aux deux. Toutefois, une
programmation soignée est nécessaire pour que cela soit fait
correctement.
Q3 Peut-on utiliser le descripteur de epoll lui-même avec
poll/epoll/select?
A3 Oui. Si un descripteur de fichier epoll a des événements en
attente, alors il indiquera qu’il est lisible.
Q4 Que se passe-t-il si on cherche à placer un descripteur de epoll
dans son propre ensemble ?
A4 L’appel à epoll_ctl(2) échouera (EINVAL). Toutefois vous pouvez
ajouter un descripteur de epoll dans un autre ensemble epoll.
Q5 Puis-je envoyer le descripteur de epoll à travers une socket Unix
vers un autre processus ?
A5 Oui, mais il n’y a aucune raison de faire ça, puisque le processus
récepteur n’aura pas de copie des descripteurs de fichier de
l’ensemble epoll.
Q6 Est-ce que la fermeture d’un descripteur le supprime
automatiquement de tous les ensembles epoll ?
A6 Oui, mais prenez note des points qui suivent. Un descripteur de
fichier est une référence vers la description d’un fichier ouvert
(voir open(2)). À chaque fois qu’un descripteur est dupliqué avec
dup(2), dup2(2), fcntl(2) F_DUPFD ou fork(2), un nouveau
descripteur de fichier qui se réfère au même fichier ouvert est
créé. Une description de fichier ouvert continue à exister jusqu’à
ce que tous les descripteurs de fichier qui s’y réfèrent soient
fermés. Un descripteur de fichier est retiré d’un ensemble epoll
seulement après que tous les descripteurs de fichier qui se
réfèrent à la description de fichier ouvert sous-jacente soient
fermés (ou avant si le descripteur est explicitement retiré en
utilisant epoll_ctl() EPOLL_CTL_DEL). Ceci signifie que même après
qu’un descripteur de fichier d’un ensemble epoll soit fermé, des
événements peuvent toujours être remontés pour ce descripteur de
fichier si d’autres descripteur de fichier, se référant à la même
description de fichier sous-jacente, restent ouvert.
Q7 Si plus d’un événement surviennent entre deux appels epoll_wait(2),
sont-ils combinés ou rapportés séparément ?
A7 Ils sont combinés.
Q8 Est-ce qu’une opération sur un descripteur affecte les événements
déjà collectés mais pas encore rapportés ?
A8 Vous pouvez faire deux choses sur un descripteur existant. Une
suppression serait sans signification dans ce cas. Une modification
revérifie les entrées-sorties disponibles.
Q9 Dois-je lire/écrire sans cesse un descripteur jusqu’à obtenir
EAGAIN avec l’attribut EPOLLET (comportement edge-triggered) ?
A9 La réception d’un événement depuis epoll_wait(2) suggère qu’un
descripteur est prêt pour l’opération d’E/S désirée. Vous devez le
considérer prêt jusqu’à ce que la prochaine lecture ou écriture
(non bloquante) remonte un EAGAIN. Quand et comment utiliser le
descripteur dépend de vous.
Pour les fichiers orientés paquet ou jeton (par exemple, une socket
datagramme ou un terminal en mode canonique), la seule façon de
détecter la fin de l’espace d’entrée-sortie pour les lectures ou
écritures est de continuer à lire ou écrire jusqu’à la réception
d’un EAGAIN.
Pour les fichiers orientés flux (par exemple, les tubes, FIFO ou
sockets en mode flux), la disponibilité des entrées-sorties
peut-être vérifiée par la quantité de données lues ou écrites avec
le descripteur. Par exemple, si vous appelez read(2) en demandant
la lecture d’une certaine quantité de données et que read(2) en
renvoie moins, vous pouvez être sûrs d’avoir consommé tout le
tampon d’entrée pour le descripteur. La même chose est vraie pour
l’appel système write(2). (Évitez cette dernière technique si vous
ne pouvez garantir que le descripteur de fichier surveillé
correspond toujours à un fichier de type flux)
Erreurs possibles et moyens de les éviter
o Famine (edge-triggered)
S’il y a un gros volume d’entrées-sorties, il est possible qu’en
essayant de les traiter, d’autres fichiers ne soient pas pris en
compte, ce qu’on appelle un cas de famine. Ce problème n’est pas
spécifique à epoll.
La solution est de maintenir une liste de descripteurs prêts et de les
marquer comme tels dans leur structure associée, permettant à
l’application de savoir quels fichiers traiter, en organisant l’ordre
au mieux. Ceci permet aussi d’ignorer les événements ultérieurs sur des
descripteurs prêts.
o Utilisation d’un cache d’événements...
Si vous utilisez un cache d’événement, ou stockez tous les descripteurs
renvoyés par epoll_wait(2), alors assurez-vous de disposer d’un moyen
de marquer dynamiquement leurs fermetures (causées par un événement
précédent). Supposons que vous recevez 100 événements de epoll_wait(2),
et que l’événement 47 implique de fermer le descripteur 13. Si vous
supprimez la structure et utilisez close(2), alors votre cache peut
encore contenir des événements pour ce descripteur, et poser des
problèmes de cohérence.
Une solution est d’invoquer, pendant le traitement de l’événement 47,
epoll_ctl(EPOLL_CTL_DEL) pour supprimer le descripteur 13, le fermer
avec close(2), et marquer sa structure associée comme supprimée. Si
vous rencontrez un autre événement pour le descripteur 13 dans votre
traitement, vous verrez qu’il a été supprimé précédemment, sans que
cela ne prête à confusion.
VERSIONS
L’API epoll a été introduite dans le noyau Linux 2.5.44. La prise en
charge par la glibc a été ajoutée dans la version 2.3.2.
CONFORMITÉ
L’API epoll est spécifique à Linux. Certains autres systèmes
fournissent des mécanismes similaires. Par exemple, FreeBSD propose
kqueue et Solaris /dev/poll.
VOIR AUSSI
epoll_create(2), epoll_create1(2), epoll_ctl(2), epoll_wait(2)
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> ».