Date création : 30-03-2008 14:17:03
 Vous êtes dans : GNU/Linux Astuces / Pages man [Section7 - Divers]
EPOLL
Index
- NOM
- SYNOPSIS
- DESCRIPTION
- NOTES
- EXEMPLE D'UTILISATION CONSEILLÉE
- QUESTIONS ET RÉPONSES
- PIÈGES POSSIBLES, ET SOLUTIONS
- CONFORMITÉ
- VERSIONS
- VOIR AUSSI
- TRADUCTION
NOM
epoll - Notifications d'événements d'entrées-sorties.
SYNOPSIS
#include <sys/epoll.h>
DESCRIPTION
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. Trois appels système sont fournis pour configurer
et commander un ensemble epoll : epoll_create(2), epoll_ctl(2),
epoll_wait(2).
Un ensemble epoll est connecté à un descripteur de fichiers créé par
epoll_create(2). L'intérêt pour certains descripteurs est ensuite
enregistré avec epoll_ctl(2). Enfin, l'attente effective démarre avec
l'appel epoll_wait(2).
NOTES
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
changement d'état (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
(fd_lect) est ajouté dans un ensemble epoll.
- 2
-
Celui qui écrit dans le tube envoie 2 Ko de données.
- 3
-
Un appel à epoll_wait(2) est effectué et renvoie fd_lect comme
descripteur de fichier prêt.
- 4
-
Le lecteur du tube lit 1 Ko de données depuis fd_lect.
- 5
-
Un appel de epoll_wait(2) est effectué.
Si le descripteur fd_lect a été ajouté à l'ensemble epoll en utilisant
l'attribut EPOLLET, l'appel epoll_wait(2), réalisé à l'étape 5, va
probablement bloquer à cause 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
fd_lect 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.
Lorsqu'on emploie l'attribut EPOLLET (Edge Triggered) de la fonction
epoll, on devrait toujours utiliser des descripteurs non-bloquants pour
éviter qu'une lecture ou une écriture bloque une tâche qui gère plusieurs
descripteurs de fichiers. L'utilisation suggérée d'epoll avec
l'interface en détection de changements (EPOLLET) est décrite ci-dessous,
avec les pièges à éviter.
-
- i
-
avec des descripteurs non-bloquants ;
- 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,
epoll est une alternative plus rapide à poll(2), et peut être employé
chaque fois que poll() 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.
EXEMPLE D'UTILISATION CONSEILLÉE
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é.
struct epoll_event ev, *events;
for(;;) {
nfds = epoll_wait(kdpfd, events, maxevents, -1);
for(n = 0; n < nfds; ++n) {
if(events[n].data.fd == listener) {
client = accept(listener, (struct sockaddr *) &local,
&addrlen);
if(client < 0){
perror("accept");
continue;
}
setnonblocking(client);
ev.events = EPOLLIN | EPOLLET;
ev.data.fd = client;
if (epoll_ctl(kdpfd, EPOLL_CTL_ADD, client, &ev) < 0) {
fprintf(stderr, "epoll set insertion error: fd=%d
",
client);
return -1;
}
}
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 ET RÉPONSES
- Q1
-
Que se passe-t-il si on ajoute deux fois le même fd dans un ensemble epoll ?
- A1
-
On aura probablement l'erreur EEXIST. Toutefois, il est possible que deux
threads puissent ajouter le même fd deux fois. Sans conséquences fâcheuses.
- Q2
-
Deux ensembles epoll peuvent-ils attendre le même fd ? Si oui, les
événements seront-ils reportés sur les deux ensembles epoll en même
temps ?
- A2
-
Oui. Toutefois, c'est peu recommandé. Oui, l'événement sera rapporté pour
les deux.
- Q3
-
Peut-on utiliser le descripteur epoll lui-même avec poll/epoll/select ?
- A3
-
Oui.
- Q4
-
Que se passe-t-il si le descripteur de epoll est inséré dans son propre
ensemble ?
- A4
-
Cela échouera. Toutefois vous pouvez ajouter le descripteur de epoll dans
un autre ensemble epoll.
- Q5
-
Puis-je envoyer le descripteur epoll à travers une socket Unix vers un
autre processus ?
- A5
-
Non.
- Q6
-
Est-ce que la fermeture d'un descripteur le supprime automatiquement d'un
ensemble epoll ?
- A6
-
Oui.
- Q7
-
Si plus d'un événement survient 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 re-vérifie les
entrées-sorties disponibles.
- Q9
-
Dois-je lire/écrire sans cesse un descripteur jusqu'à obtenir EAGAIN avec
l'attribut EPOLLET (Edge Triggered behaviour) ?
- A9
-
Non. 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'au prochain EAGAIN. Quand et comment utiliser le
descripteur dépend de vous. De plus, 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).
PIÈGES POSSIBLES, ET SOLUTIONS
- 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 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 un descripteur prêt.
- 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(), 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,
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.
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.
VERSIONS
epoll(7) est une API introduite dans Linux 2.5.44. Son interface devrait
être finalisée depuis le 2.5.66.
VOIR AUSSI
epoll_create(2), epoll_ctl(2), epoll_wait(2)
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.
La traduction de cette page de manuel est basée sur les traductions
disponibles sur http://manpagesfr.free.fr/,
mais est gérée par l'équipe francophone de traduction de Debian
au travers de la liste de discussion debian-l10n-french.
Veuillez signaler toute erreur de traduction 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> ».
|