epoll(7) | Miscellaneous Information Manual | epoll(7) |
epoll – Notifications d'événements d'entrées et sorties
#include <sys/epoll.h>
L'interface de programmation (API) epoll réalise une tâche similaire à poll(2) : surveiller plusieurs descripteurs de fichier pour voir si des E/S y sont possibles. L'API epoll peut être déclenchée par changement de niveau ou par changement d'état et s'adapte bien à un grand nombre de descripteurs surveillés.
Le concept central de l’API epoll est l’instance d’epoll, une structure interne au noyau qui, du point de vue espace utilisateur, peut être considérée comme un conteneur pour deux listes :
Les appels système suivants sont fournis pour créer et gérer une instance d’epoll :
L'interface de distribution d'événements d’epoll est capable de se comporter en détection de changement de niveau (Level Triggered — LT) ou 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 :
Si le descripteur rfd a été ajouté à l'ensemble epoll en utilisant l'attribut EPOLLET (détection de changement d'état), 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 le tampon d'entrée du fichier tandis que 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 détectés par changement d’état délivre les événements seulement lorsque des événements surviennent dans le descripteur de 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 l’étape 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 affame une tâche qui gère plusieurs descripteurs de fichier. L'utilisation préconisée d'epoll comme interface en détection par changement d’état (EPOLLET) est la suivante :
En revanche, lorsqu'il est utilisé avec l'interface en détection par changement 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.
Puisque même dans un epoll de type détection le changement d'état, 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.
Si plusieurs threads (ou processus si les processus enfant ont hérité du descripteur de fichier d’epoll à travers fork(2)) sont bloqués dans epoll_wait(2) en attente du même descripteur de fichier d’epoll et qu’un descripteur de fichier dans la liste interest, qui est marqué pour une notification par détection de changement d'état (EPOLLET), devienne prêt, seul un des threads (ou processus) est réveillé de epoll_wait(2). Cela fournit une optimisation utile pour éviter la bousculade de réveils (thundering herd) dans certain scénarios.
Si le système est en mode autosleep à l’aide de /sys/power/autosleep et qu’un événement survient et sort le périphérique de sa veille, le pilote de périphérique ne gardera le périphérique actif que jusqu’à la mise en file d’attente de l’événement. Pour garder le périphérique actif jusqu’au traitement de l’événement, l’attribut EPOLLWAKEUP d’epoll_ctl(2) doit être utilisé.
Quand l’attribut EPOLLWAKEUP est défini dans le champ events pour une struct epoll_event, le système sera gardé actif à partir du moment où l’événement est mis en file d’attente, à l’aide de l’appel epoll_wait(2) qui renvoie l’événement jusqu’à l’appel epoll_wait(2) suivant. Si l’événement doit garder le système actif au delà de ce moment, alors un wake_lock séparé devrait être pris avant le second appel à epoll_wait(2).
Les interfaces suivantes peuvent être utilisées pour limiter la quantité de mémoire du noyau utilisée par epoll :
Tandis que l'utilisation d’epoll avec un déclenchement par changement de niveau correspond à la même sémantique que poll(2), le déclenchement par changement d'état nécessite plus de clarification pour éviter des décrochages dans la boucle d’évènements de l’application. Dans cet exemple, l’écouteur emploie un socket non bloquant sur lequel listen(2) a été appelé. La fonction do_use_fd() va utiliser le nouveau descripteur de fichier jusqu'à ce qu’EAGAIN soit renvoyé par read(2) ou par write(2). Une application d’automate fini piloté par les évènements devrait, après réception d'EAGAIN, enregistrer l'état en cours, afin que lors de l’appel suivant à do_use_fd(), elle continue avec le read(2) ou le write(2) là où elle s'est arrêtée.
#define MAX_EVENTS 10 struct epoll_event ev, events[MAX_EVENTS]; int listen_sock, conn_sock, nfds, epollfd; /* Code to set up listening socket, 'listen_sock', (socket(), bind(), listen()) omitted. */ epollfd = epoll_create1(0); if (epollfd == -1) { perror("epoll_create1"); 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_wait"); exit(EXIT_FAILURE); } for (n = 0; n < nfds; ++n) { if (events[n].data.fd == listen_sock) { conn_sock = accept(listen_sock, (struct sockaddr *) &addr, &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 d’epoll (EPOLL_CTL_ADD) après, 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.
The epoll API was introduced in Linux kernel 2.5.44. Support was added in glibc 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.
The set of file descriptors that is being monitored via an epoll file descriptor can be viewed via the entry for the epoll file descriptor in the process's /proc/pid/fdinfo directory. See proc(5) for further details.
L’opération KCMP_EPOLL_TFD de kcmp(2) peut être utilisée pour tester si un descripteur de fichier est présent dans une instance d’epoll.
epoll_create(2), epoll_create1(2), epoll_ctl(2), epoll_wait(2), poll(2), select(2)
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> et Jean-Paul Guillonneau <guillonneau.jeanpaul@free.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.
5 février 2023 | Pages du manuel de Linux 6.03 |