Programmation réseau Linux (utilisation de la fonction epoll)


Préface

Dans cet article, nous expliquons comment utiliser la fonction epoll. Par rapport au sondage, epoll a amélioré les performances.

1. Explication du concept et des caractéristiques d'epoll

epoll est un mécanisme de multiplexage hautes performances sous Linux qui surveille un grand nombre de descripteurs de fichiers et avertit les applications lorsqu'elles sont prêtes. Il est encore optimisé et amélioré sur la base de la sélection et du sondage.

epoll 的主要特点包括:

1. Il n'y a pas de limite sur le nombre de descripteurs de fichiers : contrairement à select et poll, epoll utilise un mécanisme de notification prêt basé sur des événements sans limite prédéfinie sur le nombre de descripteurs de fichiers, qui peut prendre en charge des connexions simultanées à plus grande échelle.

2. Notification d'événement efficace: epoll utilise la structure de données d'événement partagée par le noyau et l'espace utilisateur pour enregistrer l'événement du descripteur de fichier dans l'espace du noyau. Lorsque l'événement est prêt, le noyau notifie directement l'événement prêt à l'espace utilisateur, éviter chaque appel nécessite une surcharge de performances liée au parcours de l'ensemble du tableau de descripteurs de fichier.

3. Ensemble d'événements prêts séparés : epoll copie les événements prêts de l'espace noyau vers l'espace utilisateur pour former un ensemble d'événements prêts séparé. Les utilisateurs peuvent parcourir directement cet ensemble pour traiter les événements prêts sans parcourir l'ensemble du tableau des descripteurs de fichiers.

4. Prise en charge du déclencheur de bord et du déclencheur de niveau : epoll fournit deux modes pour gérer les événements, l'un est le mode de déclenchement de bord (EPOLLET), qui informe l'application uniquement lorsque l'état change, et l'autre est le mode de déclenchement de niveau (par défaut). averti lorsque l'événement est prêt.

5. Réduire la surcharge de copie de mémoire : epoll utilise la technologie de mappage de mémoire pour éviter la surcharge de copie des données d'événement du noyau vers l'espace utilisateur pour chaque appel, réduisant ainsi le nombre d'appels système et la surcharge de copie de mémoire.

6. Prise en charge d'un contrôle de délai d'attente de plus grande précision : contrairement au sondage, les paramètres de délai d'attente d'epoll sont en millisecondes et nanosecondes, offrant un contrôle de délai d'attente de plus grande précision.

总体来说,epoll 在性能上相比于 select 和 poll 有较大的优势,特别适用于高并发场景下的网络编程。它的高效事件就绪通知、支持大规模并发连接、较低的内存拷贝开销以及较高的超时精度,使得它成为开发高性能服务器和网络应用的首选机制。

2. Mécanisme de mise en œuvre d'epoll

epoll est implémenté à l'aide d'un arbre rouge-noir (Red-Black Tree). epoll est un mécanisme efficace de notification d'événements fourni par le système d'exploitation Linux, utilisé pour gérer un grand nombre de connexions simultanées. Il peut surveiller les changements d'état de plusieurs descripteurs de fichiers et effectuer le traitement correspondant via des fonctions de rappel lorsque les descripteurs de fichiers sont prêts.

Dans le noyau Linux, epoll utilise une arborescence rouge-noir comme structure de données principale pour maintenir un ensemble de descripteurs de fichiers enregistrés. Un arbre rouge-noir est un arbre de recherche binaire auto-équilibré avec une complexité temporelle plus rapide pour les opérations d'insertion, de suppression et de recherche. En utilisant des arbres rouge-noir, epoll peut récupérer et gérer efficacement un grand nombre de descripteurs de fichiers.

Lorsqu'un événement se produit sur le descripteur de fichier, epoll localise rapidement le nœud correspondant grâce à l'opération de recherche de l'arborescence rouge-noir et déclenche la fonction de rappel enregistrée pour traiter l'événement. La raison de l'utilisation d'un arbre rouge-noir est qu'il peut maintenir un bon équilibre et garantir que la complexité temporelle dans le pire des cas des opérations de recherche, d'insertion et de suppression est O (log n), garantissant ainsi les hautes performances et l'évolutivité d'epoll. .

En résumé, epoll est implémenté en utilisant une arborescence rouge-noir comme structure de données sous-jacente, ce qui lui permet de fournir un mécanisme de notification d'événements efficace lorsqu'il s'agit de gérer un grand nombre de connexions simultanées.

3. Explication des fonctions liées à epoll

1.fonction epoll_create

Prototype de fonction :

int epoll_create(int size);

La fonction epoll_create crée une instance epoll et renvoie un descripteur de fichier utilisé pour identifier l'instance epoll. Le paramètre size est une indication, indiquant la limite supérieure du nombre de descripteurs de fichiers que l'instance epoll peut surveiller. Mais dans la plupart des cas, ce paramètre sera ignoré et n’importe quelle valeur pourra être transmise.

Le descripteur de fichier renvoyé peut être utilisé pour effectuer des opérations ultérieures sur l'instance epoll, telles que l'enregistrement, la modification et la suppression d'événements de descripteur de fichier.

Il convient de noter que la fonction epoll_create renvoie un entier non négatif en cas de succès, indiquant le descripteur de fichier de l'instance epoll. Si une erreur se produit, la valeur de retour est -1 et le code d'erreur errno est défini pour indiquer l'erreur spécifique. taper.

2.Fonction epoll_ctl

Prototype de fonction :

int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);

La fonction epoll_ctl exploite une instance epoll spécifique via le paramètre epfd spécifié. Le paramètre op indique le type d'opération, qui peut être l'un des trois suivants :

EPOLL_CTL_ADD : ajoutez le descripteur de fichier spécifié fd à l'instance epoll et enregistrez les événements correspondants. De cette façon, l'application est avertie lorsqu'un événement sur ce descripteur de fichier est prêt.

EPOLL_CTL_MOD : Modifier l'événement correspondant au descripteur de fichier fd enregistré dans l'instance epoll. Vous pouvez modifier le type d'événement, les événements préoccupants, les données utilisateur associées, etc.

EPOLL_CTL_DEL : Supprimez le descripteur de fichier fd enregistré dans l'instance epoll.

Le paramètre fd est le descripteur de fichier cible, qui est utilisé pour spécifier le descripteur de fichier sur lequel il faut opérer.

Le paramètre event est un pointeur vers la structure struct epoll_event, utilisée pour spécifier la configuration liée aux événements. Cette structure contient deux membres :

Événements uint32_t : indique le type d'événement enregistré, qui peut être EPOLLIN (événement lisible), EPOLLOUT (événement inscriptible), EPOLLRDHUP (le homologue ferme la connexion), EPOLLPRI (les données urgentes peuvent être lues), EPOLLERR (événement d'erreur), etc. Peut être combiné à l’aide de masques de bits.

Données epoll_data_t : utilisées pour stocker les informations sur les données utilisateur, qui peuvent être la valeur du descripteur de fichier lui-même ou un pointeur de structure de données défini par l'utilisateur.

La valeur de retour de la fonction est 0, ce qui signifie que l'opération a réussi, -1 signifie qu'une erreur s'est produite et les informations d'erreur spécifiques peuvent être obtenues en vérifiant la variable errno.

3.Fonction epoll_wait

Prototype de fonction :

int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);

Le paramètre epfd est le descripteur de l'instance epoll, qui spécifie l'instance epoll qui attend les événements.

Le paramètre events est un tableau utilisé pour stocker les informations sur les événements, et chaque élément du tableau est une structure de type struct epoll_event, qui est utilisée pour stocker les descripteurs de fichiers prêts et les informations sur les événements correspondants.

Le paramètre maxevents indique la taille du tableau d'événements, c'est-à-dire le nombre maximum d'événements pouvant être attendus.

Le paramètre timeout est le délai d'attente en millisecondes. Il détermine le comportement bloquant de la fonction epoll_wait :

Si le délai d'attente est défini sur -1, cela signifie bloquer indéfiniment jusqu'à ce qu'un événement se produise.

Si le délai d'attente est défini sur 0, cela signifie non bloquant et l'événement actuellement prêt sera renvoyé immédiatement. Si aucun événement n'est prêt, 0 sera renvoyé.

Si le délai d'attente est défini sur un entier positif, cela signifie bloquer et attendre le temps spécifié avant de revenir, et si l'événement n'est pas prêt dans le délai d'attente, renvoyer 0.

La fonction epoll_wait renvoie le nombre de descripteurs de fichier de l'événement ready en cas de succès, renvoie -1 si une erreur se produit et définit le code d'erreur errno pour indiquer le type d'erreur spécifique.

4. epoll implémente un serveur simultané

当使用epoll实现并发服务器时,通常的步骤包括以下几个主要环节:

1. Créer un socket : utilisez la fonction socket pour créer un socket d'écoute pour accepter les demandes de connexion client.

2. Bind socket : utilisez la fonction bind pour lier le socket d’écoute à une adresse IP et un port spécifiques.

3. Connexion en écoute : utilisez la fonction d'écoute pour commencer à écouter les demandes de connexion et spécifiez le nombre maximum de connexions que le serveur peut accepter.

4. Créez une instance epoll : utilisez la fonction epoll_create pour créer une instance epoll et renvoyer un descripteur de fichier.

5. Ajoutez le socket d'écoute à l'instance epoll : utilisez la fonction epoll_ctl pour ajouter le socket d'écoute à l'instance epoll et enregistrez votre préoccupation pour l'événement de lecture.

6. Entrez dans la boucle d'événement : Appelez la fonction epoll_wait dans une boucle pour attendre que l'événement se produise. Cette fonction bloquera le thread actuel jusqu'à ce qu'un événement se produise. Une fois qu'un événement se produit, il renverra une liste d'événements prêts.

7. Traiter les événements prêts : parcourez la liste des événements prêts et traitez chaque événement. Selon le type d'événement, des opérations telles que l'acceptation d'une connexion, la lecture de données, l'envoi de données ou la fermeture d'une connexion peuvent être effectuées.

8. Ajoutez ou supprimez des descripteurs de fichiers selon vos besoins : Après avoir traité un événement, vous pouvez utiliser la fonction epoll_ctl pour ajouter ou supprimer dynamiquement des descripteurs de fichiers selon vos besoins afin de continuer à écouter d'autres événements.

9. Répétez les étapes 6 à 8 : continuez à exécuter les étapes 6 à 8 en boucle pour traiter les nouveaux événements prêts jusqu'à ce que le serveur s'arrête activement ou qu'une condition d'erreur se produise.

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/epoll.h>

#define MAX_EVENTS  1024 //最多可以等待多少个事件

int main()
{
    
    
    int server = 0;
    struct sockaddr_in saddr = {
    
    0};
    int client = 0;
    struct sockaddr_in caddr = {
    
    0};
    socklen_t asize = 0;
    int len = 0;
    char buf[32] = {
    
    0};
    int maxfd;
    int ret = 0;
    int i = 0;

    server = socket(PF_INET, SOCK_STREAM, 0);

    if( server == -1 )
    {
    
    
        printf("server socket error\n");
        return -1;
    }

    saddr.sin_family = AF_INET;
    saddr.sin_addr.s_addr = htonl(INADDR_ANY);
    saddr.sin_port = htons(8888);

    if( bind(server, (struct sockaddr*)&saddr, sizeof(saddr)) == -1 )
    {
    
    
        printf("server bind error\n");
        return -1;
    }

    if( listen(server, 128) == -1 )
    {
    
    
        printf("server listen error\n");
        return -1;
    }

    printf("server start success\n");


    struct epoll_event event, events[MAX_EVENTS];
    /*创建epoll*/
    int epollInstance = epoll_create1(0);
    if (epollInstance == -1) 
    {
    
    
        printf("Failed to create epoll instance\n");
    }
    /*将服务器添加进入event中*/
    event.events = EPOLLIN;
    event.data.fd = server;

    if (epoll_ctl(epollInstance, EPOLL_CTL_ADD, server, &event) == -1) 
    {
    
    
        printf("Failed to add server socket to epoll instance");
    }        

    while( 1 )
    {
    
            
        int numEventsReady = epoll_wait(epollInstance, events, MAX_EVENTS, -1);
        if (numEventsReady == -1) 
        {
    
    
            printf("Failed to wait for events");
            return -1;
        }

        for(i = 0; i < numEventsReady; i++)
        {
    
    
            if(events[i].data.fd == server)
            {
    
    
                /*有客户端连接上来了*/
                asize = sizeof(caddr);  
                client = accept(server, (struct sockaddr*)&caddr, &asize);
                printf("client is connect\n");

                event.events = EPOLLIN | EPOLLET;
                event.data.fd = client;

                if (epoll_ctl(epollInstance, EPOLL_CTL_ADD, client, &event) == -1) 
                {
    
    
                    printf("Failed to add client socket to epoll instance");
                    return -1;
                }                
            }
            else
            {
    
    
                /*处理客户端的请求*/
                len = read(events[i].data.fd, buf, 1024);
                if(len == 0)
                {
    
    
                    printf("client is disconnect\n");
                    close(events[i].data.fd);
                }
                else
                {
    
    
                    /*对接收到的数据进行处理*/
                    printf("read buf : %s\n", buf);
                    write(events[i].data.fd, buf, len);
                }
            }
        }        


    }
    
    close(server);

    return 0;
}

Résumer

Cet article se termine ici et l'article suivant continue d'expliquer les connaissances en programmation réseau Linux.

Je suppose que tu aimes

Origine blog.csdn.net/m0_49476241/article/details/132376808
conseillé
Classement