GNU/Linux >> Tutoriels Linux >  >> Linux

Comment gérer les événements de socket Linux POLLERR, POLLHUP et POLLNVAL ?

POLLNVAL signifie que la valeur du descripteur de fichier n'est pas valide. Cela indique généralement une erreur dans votre programme, mais vous pouvez vous fier à poll retour POLLNVAL si vous avez fermé un descripteur de fichier et que vous n'avez ouvert aucun fichier depuis lors, cela pourrait avoir réutilisé le descripteur.

POLLERR est similaire aux événements d'erreur de select . Il indique qu'un read ou write call renverrait une condition d'erreur (par exemple, une erreur d'E/S). Cela n'inclut pas les données hors bande qui select signaux via son errorfds masque mais poll signaux via POLLPRI .

POLLHUP signifie essentiellement que ce qui se trouve à l'autre extrémité de la connexion a fermé son extrémité de la connexion. POSIX le décrit comme

L'appareil a été déconnecté. Cet événement et POLLOUT sont mutuellement exclusifs ; un flux ne peut jamais être accessible en écriture si un raccrochage s'est produit.

C'est assez clair pour un terminal :le terminal est parti (même événement qui génère un SIGHUP :la session modem s'est terminée, la fenêtre de l'émulateur de terminal s'est fermée, etc.). POLLHUP n'est jamais envoyé pour un dossier régulier. Pour les tuyaux et les prises, cela dépend du système d'exploitation. Linux définit POLLHUP lorsque le programme à la fin de l'écriture d'un tube ferme le tube et définit POLLIN|POLLHUP lorsque l'autre extrémité d'un socket a fermé le socket, mais POLLIN uniquement pour un arrêt de socket. Ensemble *BSD récent POLLIN|POLLUP lorsque la fin d'écriture d'un tube ferme le tube, et le comportement des sockets est plus variable.


Un POLLHUP signifie que la prise n'est plus connectée. Dans TCP, cela signifie que FIN a été reçu et envoyé.

Un POLLERR signifie que le socket a reçu une erreur asynchrone. Dans TCP, cela signifie généralement qu'un RST a été reçu ou envoyé. Si le descripteur de fichier n'est pas un socket, POLLERR peut signifier que l'appareil ne prend pas en charge l'interrogation.

Pour les deux conditions ci-dessus, le descripteur de fichier de socket est toujours ouvert et n'a pas encore été fermé (mais shutdown() peut-être déjà été appelé). Un close() sur le descripteur de fichier libérera les ressources qui sont encore réservées au nom du socket. En théorie, il devrait être possible de réutiliser le socket immédiatement (par exemple, avec un autre connect() appel).

Un POLLNVAL signifie que le descripteur de fichier socket n'est pas ouvert. Ce serait une erreur de close() il.


Cela dépend de la nature exacte de l'erreur. Utilisez getsockopt() pour voir le problème :

int error = 0;
socklen_t errlen = sizeof(error);
getsockopt(fd, SOL_SOCKET, SO_ERROR, (void *)&error, &errlen);

Valeurs :http://www.xinotes.net/notes/note/1793/

Le plus simple est de supposer que le socket n'est plus utilisable dans tous les cas et de le fermer.


Exemple FIFO minimal

Une fois que vous comprenez quand ces conditions se produisent, il devrait être facile de savoir quoi en faire.

sondage.c

#define _XOPEN_SOURCE 700
#include <fcntl.h> /* creat, O_CREAT */
#include <poll.h> /* poll */
#include <stdio.h> /* printf, puts, snprintf */
#include <stdlib.h> /* EXIT_FAILURE, EXIT_SUCCESS */
#include <unistd.h> /* read */

int main(void) {
    char buf[1024];
    int fd, n;
    short revents;
    struct pollfd pfd;

    fd = open("poll0.tmp", O_RDONLY | O_NONBLOCK);
    pfd.fd = fd;
    pfd.events = POLLIN;
    while (1) {
        puts("loop");
        poll(&pfd, 1, -1);
        revents = pfd.revents;
        if (revents & POLLIN) {
            n = read(pfd.fd, buf, sizeof(buf));
            printf("POLLIN n=%d buf=%.*s\n", n, n, buf);
        }
        if (revents & POLLHUP) {
            printf("POLLHUP\n");
            close(pfd.fd);
            pfd.fd *= -1;
        }
        if (revents & POLLNVAL) {
            printf("POLLNVAL\n");
        }
        if (revents & POLLERR) {
            printf("POLLERR\n");
        }
    }
}

GitHub en amont.

Compiler avec :

gcc -o poll.out -std=c99 poll.c

Utilisation :

sudo mknod -m 666 poll0.tmp p
./poll.out

Sur un autre shell :

printf a >poll0.tmp

SONDAGE

Si vous ne modifiez pas la source :./poll.out sorties :

loop
POLLIN n=1 buf=a
loop
POLLHUP
loop

Donc :

  • POLLIN se produit lorsque l'entrée devient disponible
  • POLLHUP se produit lorsque le fichier est fermé par le printf
  • close(pfd.fd); et pfd.fd *= -1; nettoyer les choses, et nous arrêtons de recevoir POLLHUP
  • poll pend pour toujours

C'est le fonctionnement normal.

Vous pouvez maintenant renvoyer le FIFO pour attendre le prochain open , ou quittez la boucle si vous avez terminé.

SONDAGE

Si vous commentez pfd.fd *= -1; :./poll.out imprime :

POLLIN n=1 buf=a
loop
POLLHUP
loop
POLLNVAL
loop
POLLNVAL
...

et boucle pour toujours.

Donc :

  • POLLIN et POLLHUP et close s'est passé comme avant
  • puisque nous n'avons pas défini pfd.fd à un nombre négatif, poll continue d'essayer d'utiliser le fd que nous avons fermé
  • cela renvoie toujours POLLNVAL pour toujours

Nous voyons donc que cela n'aurait pas dû se produire et indique un bogue dans votre code.

POLLERR

Je ne sais pas comment générer un POLLERR avec les FIFO. Faites-moi savoir s'il y a moyen. Mais cela devrait être possible avec file_operations d'un pilote de périphérique.

Testé dans Ubuntu 14.04.


Linux
  1. Comment Linux sauve les ordinateurs lents (et la planète)

  2. Comment gérer les bibliothèques dynamiques et statiques sous Linux

  3. Comment vérifier la version du système d'exploitation et de Linux

  4. Comment déplacer des fichiers et des répertoires vers le dossier parent sous Linux ?

  5. Comment déterminer le temps de connexion du socket sous Linux

Comment effacer (vider) le cache DNS sous Windows, MacOS et Linux

Comment utiliser et tirer le meilleur parti de la commande fuser sous Linux

Comment rechercher et répertorier récursivement les fichiers par date sous Linux

Comment trouver le PID et le PPID d'un processus sous Linux

Comment installer et utiliser la commande Ack sous Linux

Linux perf :comment utiliser la commande et le profileur