GNU/Linux >> Tutoriales Linux >  >> Linux

¿Cómo manejar los eventos de socket de Linux POLLERR, POLLHUP y POLLNVAL?

POLLNVAL significa que el valor del descriptor de archivo no es válido. Por lo general, indica un error en su programa, pero puede confiar en poll devolviendo POLLNVAL si ha cerrado un descriptor de archivo y no ha abierto ningún archivo desde entonces, es posible que haya reutilizado el descriptor.

POLLERR es similar a los eventos de error de select . Indica que un read o write call devolvería una condición de error (por ejemplo, error de E/S). Esto no incluye datos fuera de banda que select señales a través de su errorfds máscara pero poll señales a través de POLLPRI .

POLLHUP básicamente significa que lo que está en el otro extremo de la conexión ha cerrado su extremo de la conexión. POSIX lo describe como

El dispositivo ha sido desconectado. Este evento y POLLOUT son mutuamente excluyentes; nunca se puede escribir en una transmisión si se ha producido un bloqueo.

Esto es bastante claro para una terminal:la terminal se ha ido (mismo evento que genera un SIGHUP:la sesión del módem se ha terminado, la ventana del emulador de terminal se ha cerrado, etc.). POLLHUP nunca se envía para un archivo regular. Para tuberías y enchufes, depende del sistema operativo. Linux establece POLLHUP cuando el programa en el extremo de escritura de una tubería cierra la tubería y establece POLLIN|POLLHUP cuando el otro extremo de un zócalo cerró el zócalo, pero POLLIN sólo para un apagado de socket. Conjunto reciente de *BSD POLLIN|POLLUP cuando el extremo de escritura de una tubería cierra la tubería, y el comportamiento de los enchufes es más variable.


Un POLLHUP significa que el enchufe ya no está conectado. En TCP, esto significa que se ha recibido y enviado FIN.

Un POLLERR significa que el socket recibió un error asíncrono. En TCP, esto normalmente significa que se ha recibido o enviado un RST. Si el descriptor de archivo no es un socket, POLLERR podría significar que el dispositivo no es compatible con el sondeo.

Para las dos condiciones anteriores, el descriptor del archivo de socket aún está abierto y aún no se ha cerrado (pero shutdown() puede que ya hayan sido llamados). Un close() en el descriptor de archivo liberará los recursos que todavía están reservados en nombre del socket. En teoría, debería ser posible reutilizar el socket inmediatamente (por ejemplo, con otro connect() llamar).

Un POLLNVAL significa que el descriptor del archivo de socket no está abierto. Sería un error close() eso.


Depende de la naturaleza exacta del error. Use getsockopt() para ver el problema:

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

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

La forma más fácil es asumir que el socket ya no se puede utilizar en ningún caso y cerrarlo.


Ejemplo FIFO mínimo

Una vez que comprenda cuándo ocurren esas condiciones, debería ser fácil saber qué hacer con ellas.

encuesta.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 ascendente.

Compilar con:

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

Uso:

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

En otro caparazón:

printf a >poll0.tmp

POLLHUP

Si no modifica la fuente:./poll.out salidas:

loop
POLLIN n=1 buf=a
loop
POLLHUP
loop

Entonces:

  • POLLIN ocurre cuando la entrada está disponible
  • POLLHUP sucede cuando el archivo es cerrado por el printf
  • close(pfd.fd); y pfd.fd *= -1; limpia las cosas y dejaremos de recibir POLLHUP
  • poll cuelga para siempre

Esta es la operación normal.

Ahora puede volver a configurar el FIFO para esperar el próximo open , o salga del bucle si ha terminado.

POLLNAL

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

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

y bucles para siempre.

Entonces:

  • POLLIN y POLLHUP y close sucedió como antes
  • ya que no configuramos pfd.fd a un número negativo, poll sigue intentando usar el fd que cerramos
  • esto sigue devolviendo POLLNVAL por siempre

Entonces vemos que esto no debería haber sucedido e indica un error en su código.

POLLERR

No sé cómo generar un POLLERR con FIFO. Déjame saber si hay manera. Pero debería ser posible con file_operations de un controlador de dispositivo.

Probado en Ubuntu 14.04.


Linux
  1. Cómo rescata Linux las computadoras lentas (y el planeta)

  2. Cómo abrir y cerrar directorios en la terminal de Linux

  3. Cómo manejar bibliotecas dinámicas y estáticas en Linux

  4. Cómo instalar y usar el comando ping en Linux

  5. ¿Cómo interpretar y corregir un error de entrada/salida en Linux?

Cómo borrar (vaciar) la caché de DNS en Windows, MacOS y Linux

Cómo usar y aprovechar al máximo el comando fuser en Linux

Cómo encontrar recursivamente y enumerar los archivos por fecha en Linux

Cómo encontrar el PID y PPID de un proceso en Linux

Cómo instalar y usar el comando Ack en Linux

Perf de Linux:cómo usar el comando y el generador de perfiles