NOM¶
epoll - Notifications d'événements d'entrées-sorties
SYNOPSIS¶
#include <sys/epoll.h>
DESCRIPTION¶
L'interface de programmation (API)
epoll réalise une tâche
similaire à
poll(2) : la surveillance de plusieurs
descripteurs de fichier pour voir si les E/S y sont possibles. L'API
epoll peut être déclenchée par niveau ou par
changement d'état, et s'adapte bien à un grand nombre de
descripteurs simultanés. Les appels système suivants sont
fournis pour créer et superviser une instance
epoll :
- *
- epoll_create(2) créé une instance epoll et
renvoie un descripteur de fichier référençant cette
instance (la dernière version d' epoll_create1(2)
étend les fonctionnalités d' epoll_create(2)).
- *
- L'intérêt pour un descripteur de fichier est ensuite
enregistré avec epoll_ctl(2). L'ensemble des descripteurs de
fichiers actuellement enregistrés pour une instance epoll
est parfois appelé un ensemble epoll.
- *
- epoll_wait(2) attend les événements d'E/S, en
bloquant le thread appelant si aucun événement n'est
actuellement disponible.
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)
- Cela définit une limite au nombre total de descripteurs de fichiers
qu'un utilisateur peut enregistrer au travers de 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). Cela é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.
Cela peut être une technique utile pour le filtrage
d'événements, si les descripteurs duplicats sont
enregistrés 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 suivants. Un descripteur de fichier est
une référence vers la description d'un fichier ouvert
(consultez 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 d'exister jusqu'à ce que
tous les descripteurs de fichier qui s'y réfèrent soient
fermés. Un descripteur de fichier n'est retiré d'un ensemble
epoll qu'après la fermeture de tous les descripteurs de
fichier qui se réfèrent à la description de fichier
ouvert sous-jacente (ou avant si le descripteur est explicitement
retiré en utilisant epoll_ctl(2) EPOLL_CTL_DEL). Cela
signifie que même après la fermeture d'un descripteur de
fichier d'un ensemble epoll, des événements peuvent
toujours être remontés pour ce descripteur de fichier si
d'autres descripteurs de fichier, se référant à la
même description de fichier sous-jacente, restent ouverts.
- 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. Cela 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.
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.65 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¶
Depuis 2010, cette traduction est maintenue à l'aide de l'outil po4a
<
http://po4a.alioth.debian.org/> par l'équipe de traduction
francophone au sein du projet perkamon
<
http://perkamon.alioth.debian.org/>.
Christophe Blaess <
http://www.blaess.fr/christophe/> (1996-2003), Alain
Portal <
http://manpagesfr.free.fr/> (2003-2006). Julien Cristau et
l'équipe francophone de traduction de Debian (2006-2009).
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> ».