GNU/Linux >> Tutoriels Linux >  >> Linux

comment select() est-il alerté qu'un fd est prêt ?

Il signale qu'il est prêt d'ici retour.

select attend des événements qui sont généralement hors du contrôle de votre programme. Essentiellement, en appelant select , votre programme dit "Je n'ai rien à faire jusqu'à ..., veuillez suspendre mon processus".

La condition que vous spécifiez est un ensemble d'événements, dont chacun vous réveillera.

Par exemple, si vous téléchargez quelque chose, votre boucle devra attendre que de nouvelles données arrivent, qu'un délai d'attente se produise si le transfert est bloqué, ou que l'utilisateur interrompe, ce qui est précisément ce que select fait.

Lorsque vous avez plusieurs téléchargements, les données arrivant sur l'une des connexions déclenchent une activité dans votre programme (vous devez écrire les données sur le disque), vous donneriez donc une liste de toutes les connexions de téléchargement à select dans la liste des descripteurs de fichiers à surveiller pour "lire".

Lorsque vous téléchargez des données quelque part en même temps, vous utilisez à nouveau select pour voir si la connexion accepte actuellement les données. Si l'autre côté est en mode commuté, il ne reconnaîtra les données que lentement, de sorte que votre tampon d'envoi local est toujours plein, et toute tentative d'écrire plus de données bloquera jusqu'à ce que l'espace tampon soit disponible, ou échouera. En passant le descripteur de fichier que nous envoyons à select en tant que descripteur "écriture", nous sommes avertis dès que de l'espace tampon est disponible pour l'envoi.

L'idée générale est que votre programme devient événementiel , c'est-à-dire qu'il réagit aux événements externes à partir d'une boucle de messages commune plutôt que d'effectuer des opérations séquentielles. Vous dites au noyau "c'est l'ensemble d'événements pour lesquels je veux faire quelque chose", et le noyau vous donne un ensemble d'événements qui se sont produits. Il est assez courant que deux événements se produisent simultanément; par exemple, un accusé de réception TCP a été inclus dans un paquet de données, cela peut rendre le même fd à la fois lisible (les données sont disponibles) et inscriptible (les données reconnues ont été supprimées du tampon d'envoi), vous devez donc être prêt à gérer tous les événements avant d'appeler le select à nouveau.

L'un des points les plus délicats est que select vous donne essentiellement la promesse qu'une invocation de read ou write ne bloquera pas, sans faire aucune garantie sur l'appel lui-même. Par exemple, si un octet d'espace tampon est disponible, vous pouvez essayer d'écrire 10 octets, et le noyau reviendra et dira "J'ai écrit 1 octet", vous devez donc être prêt à gérer également ce cas. Une approche typique consiste à avoir un tampon "données à écrire sur ce fd", et tant qu'il n'est pas vide, le fd est ajouté à l'ensemble d'écriture et l'événement "inscriptible" est géré en essayant d'écrire tout les données actuellement dans la mémoire tampon. Si le tampon est vide par la suite, très bien, sinon, attendez simplement à nouveau sur "inscriptible".

L'ensemble "exceptionnel" est rarement utilisé - il est utilisé pour les protocoles qui ont des données hors bande où il est possible que le transfert de données soit bloqué, tandis que d'autres données doivent passer. Si votre programme ne peut pas actuellement accepter les données d'un descripteur de fichier "lisible" (par exemple, vous téléchargez et le disque est plein), vous ne souhaitez pas inclure le descripteur dans l'ensemble "lisible", car vous ne pouvez pas gérer l'événement et select reviendrait immédiatement si elle était à nouveau invoquée. Si le destinataire inclut le fd dans l'ensemble "exceptionnel", et que l'expéditeur demande à sa pile IP d'envoyer un paquet avec des données "urgentes", le destinataire est alors réveillé et peut décider de supprimer les données non gérées et de se resynchroniser avec l'expéditeur. . Le telnet protocole l'utilise, par exemple, pour la gestion de Ctrl-C. À moins que vous ne conceviez un protocole qui nécessite une telle fonctionnalité, vous pouvez facilement l'omettre sans aucun mal.

Exemple de code obligatoire :

#include <sys/types.h>
#include <sys/select.h>

#include <unistd.h>

#include <stdbool.h>

static inline int max(int lhs, int rhs) {
    if(lhs > rhs)
        return lhs;
    else
        return rhs;
}

void copy(int from, int to) {
    char buffer[10];
    int readp = 0;
    int writep = 0;
    bool eof = false;
    for(;;) {
        fd_set readfds, writefds;
        FD_ZERO(&readfds);
        FD_ZERO(&writefds);

        int ravail, wavail;
        if(readp < writep) {
            ravail = writep - readp - 1;
            wavail = sizeof buffer - writep;
        }
        else {
            ravail = sizeof buffer - readp;
            wavail = readp - writep;
        }

        if(!eof && ravail)
            FD_SET(from, &readfds);
        if(wavail)
            FD_SET(to, &writefds);
        else if(eof)
            break;
        int rc = select(max(from,to)+1, &readfds, &writefds, NULL, NULL);
        if(rc == -1)
            break;
        if(FD_ISSET(from, &readfds))
        {
            ssize_t nread = read(from, &buffer[readp], ravail);
            if(nread < 1)
                eof = true;
            readp = readp + nread;
        }
        if(FD_ISSET(to, &writefds))
        {
            ssize_t nwritten = write(to, &buffer[writep], wavail);
            if(nwritten < 1)
                break;
            writep = writep + nwritten;
        }
        if(readp == sizeof buffer && writep != 0)
            readp = 0;
        if(writep == sizeof buffer)
            writep = 0;
    }
}

Nous essayons de lire si nous avons de l'espace tampon disponible et qu'il n'y a pas eu de fin de fichier ou d'erreur côté lecture, et nous essayons d'écrire si nous avons des données dans le tampon; si la fin du fichier est atteinte et que le tampon est vide, alors nous avons terminé.

Ce code se comportera clairement de manière sous-optimale (c'est un exemple de code), mais vous devriez pouvoir voir qu'il est acceptable que le noyau fasse moins que ce que nous avons demandé à la fois en lecture et en écriture, auquel cas nous revenons simplement en arrière et disons "chaque fois que vous êtes prêt", et que nous ne lisons ou n'écrivons jamais sans nous demander s'il bloquera.


À partir de la même page de manuel :

À la sortie, les ensembles sont modifiés sur place pour indiquer quels descripteurs de fichiers ont réellement changé de statut.

Utilisez donc FD_ISSET() sur les ensembles passés à sélectionner pour déterminer quels FD sont devenus prêts.


Linux
  1. Pourquoi les données sont importantes et comment les protéger

  2. Comment les pipelines limitent-ils l'utilisation de la mémoire ?

  3. Comment installer Duplicity sur Ubuntu ?

  4. Comment ça marche ? Que fait rm ?

  5. Comment générer des données netflow sous Linux

Comment se connecter à Plesk

PHP MySQL Sélectionner les données

Comment fusionner des données sous Linux

Comment ajouter une source de données à Redash

Comment installer ISPConfig sur CentOS 7.x ?

Comment supprimer un fichier de données AWStats