Pourquoi epoll est-il plus rapide que select?
j'ai vu beaucoup de comparaisons qui disent que select doit parcourir la liste fd, et c'est lent. Mais pourquoi ne pas epoll faire cela?
2 réponses
il y a beaucoup de fausses informations à ce sujet, mais la vraie raison est la suivante:
un serveur typique pourrait traiter, disons, 200 connexions. Il servira chaque connexion qui a besoin d'avoir des données écrites ou lues et puis il aura besoin d'attendre jusqu'à ce qu'il y ait plus de travail à faire. Tandis qu'il attend, il doit être interrompu si des données sont reçues sur l'un de ces 200 connexions.
avec select
, le noyau doit ajouter le processus à 200 listes d'attente, une pour chaque connexion. Pour ce faire, il faut un "coup de pouce" pour rattacher le processus à la liste d'attente. Lorsque le processus se réveille enfin, il doit être supprimé des 200 listes d'attente et tous ces maillons doivent être libérés.
par contraste, avec epoll
, la prise epoll
a elle-même une liste d'attente. Le processus doit être mis sur une seule liste d'attente en utilisant un seul thunk. Lorsque le processus se réveille, il doit être retiré à partir d'une seule liste d'attente et un seul Machin doit être libéré.
pour être clair, avec epoll
, la prise epoll
elle-même doit être fixée à chacune de ces 200 connexions. Mais cela se fait une fois, pour chaque connexion, quand elle est acceptée en premier lieu. Et ceci est démoli une fois, pour chaque connexion, quand il est enlevé. En revanche, chaque appel à select
qui bloque devez ajouter le processus pour chaque file d'attente pour chaque socket surveillé.
Ironiquement, avec select
, le coût le plus important vient de vérifier si les douilles qui n'ont pas eu d'activité ont eu une activité. Avec epoll
, il n'est pas nécessaire de vérifier les sockets qui n'ont eu aucune activité car s'ils avaient eu de l'activité, ils auraient informé la socket epoll
lorsque cette activité s'est produite. Dans un sens, select
scrute chaque socket chaque fois que vous appelez select
pour voir s'il y a une activité tandis que epoll
l'installe de sorte que l'activité de socket lui-même avise le processus.
la principale différence entre epoll
et select
est que dans select()
la liste des descripteurs de fichier à attendre n'existe que pour la durée d'un seul appel select()
, et la tâche d'appel ne reste sur les files d'attente des sockets que pour la durée d'un seul appel. Dans epoll
, d'un autre côté, vous créez un descripteur de fichier unique qui regroupe les événements à partir de plusieurs autres descripteurs de fichier que vous voulez attendre, et donc la liste des fd surveillés est longue durée, et les tâches restent sur les files d'attente socket à travers plusieurs appels système. De plus, comme un fd epoll
peut être partagé entre plusieurs tâches, il ne s'agit plus d'une seule tâche dans la file d'attente, mais d'une structure qui contient elle-même une autre file d'attente, contenant tous les processus qui attendent actuellement le fd epoll
. (En termes d'implémentation, ceci est résumé par les files d'attente des sockets contenant un pointeur de fonction et un pointeur de données void*
pour passer à cette fonction).
pour expliquer la mécanique un peu plus:
- An
epoll
le descripteur de fichier a unstruct eventpoll
privé qui garde la trace des fd qui sont attachés à ce fd.struct eventpoll
a également une file d'attente qui garde la trace de tous les processus qui sont actuellementepoll_wait
ing sur ce fd.struct epoll
a également une liste de tous les descripteurs de fichier qui sont actuellement disponibles pour la lecture ou l'écriture. - quand vous ajouter un descripteur de fichier à un fd
epoll
en utilisantepoll_ctl()
,epoll
ajoute lestruct eventpoll
à la file d'attente de ce fd. Il vérifie également si le fd est actuellement prêt pour le traitement et l'ajoute à la liste des prêts, si c'est le cas. - lorsque vous attendez sur un
epoll
fd en utilisantepoll_wait
, le noyau vérifie d'abord la liste des fichiers prêts, et retourne immédiatement si des descripteurs de fichiers sont déjà prêts. Si non, il s'ajoute à la file d'attente simple à l'intérieur destruct eventpoll
, et va dormir. - Lorsqu'un événement se produit sur une socket qui est
epoll()
ed, il appelle leepoll
callback, qui ajoute le descripteur de fichier à la liste de prêt, et réveille également tous les serveurs qui sont en attente sur cestruct eventpoll
.
de toute évidence, il faut bien verrouiller struct eventpoll
et les différentes listes et Files d'attente, mais c'est un détail de mise en œuvre.
l'important ce qu'il faut noter, c'est qu'à aucun moment au-dessus, je n'ai décrit une étape qui boucle tous les descripteurs de fichier d'intérêt. En étant entièrement basé sur les événements et en utilisant un ensemble de fd de longue durée et une liste prête, epoll peut éviter de prendre jamais O (n) temps pour une opération, où n est le nombre de descripteurs de fichier étant surveillé.