GNU/Linux >> Linux Esercitazione >  >> Linux

Come gestire il socket Linux revent POLLERR, POLLHUP e POLLNVAL?

POLLNVAL significa che il valore del descrittore di file non è valido. Di solito indica un errore nel tuo programma, ma puoi fare affidamento su poll restituendo POLLNVAL se hai chiuso un descrittore di file e da allora non hai aperto alcun file, ciò potrebbe aver riutilizzato il descrittore.

POLLERR è simile agli eventi di errore di select . Indica che un read o write call restituirebbe una condizione di errore (ad es. errore di I/O). Questo non include i dati fuori banda quali select segnali tramite il suo errorfds mask ma poll segnali tramite POLLPRI .

POLLHUP significa sostanzialmente che ciò che è all'altro capo della connessione ha chiuso la sua estremità della connessione. POSIX lo descrive come

Il dispositivo è stato disconnesso. Questo evento e POLLOUT si escludono a vicenda; uno stream non può mai essere scrivibile se si è verificato un blocco.

Questo è abbastanza chiaro per un terminale:il terminale è andato via (stesso evento che genera un SIGHUP:la sessione del modem è stata terminata, la finestra dell'emulatore di terminale è stata chiusa, ecc.). POLLHUP non viene mai inviato per un file normale. Per tubi e prese, dipende dal sistema operativo. Linux imposta POLLHUP quando il programma all'estremità di scrittura di una pipe chiude la pipe e imposta POLLIN|POLLHUP quando l'altra estremità di un socket chiudeva il socket, ma POLLIN solo per un arresto della presa. Set *BSD recente POLLIN|POLLUP quando l'estremità di scrittura di una pipe chiude la pipe e il comportamento per i socket è più variabile.


Un POLLHUP significa che la presa non è più collegata. In TCP, questo significa che FIN è stato ricevuto e inviato.

Un POLLERR significa che il socket ha ricevuto un errore asincrono. In TCP, questo in genere significa che è stato ricevuto o inviato un RST. Se il descrittore di file non è un socket, POLLERR potrebbe significare che il dispositivo non supporta il polling.

Per entrambe le condizioni di cui sopra, il descrittore di file socket è ancora aperto e non è stato ancora chiuso (ma shutdown() potrebbe essere già stato chiamato). Un close() sul descrittore di file rilascerà le risorse che sono ancora riservate per conto del socket. In teoria, dovrebbe essere possibile riutilizzare il socket immediatamente (ad esempio, con un altro connect() chiamata).

Un POLLNVAL significa che il descrittore del file socket non è aperto. Sarebbe un errore close() esso.


Dipende dalla natura esatta dell'errore. Usa getsockopt() per vedere il problema:

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

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

Il modo più semplice è presumere che il socket non sia più utilizzabile in ogni caso e chiuderlo.


Esempio FIFO minimo

Una volta capito quando si verificano queste condizioni, dovrebbe essere facile sapere cosa farne.

sondaggio.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 a monte.

Compila con:

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

Utilizzo:

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

Su un'altra shell:

printf a >poll0.tmp

POLLHUP

Se non modifichi la fonte:./poll.out uscite:

loop
POLLIN n=1 buf=a
loop
POLLHUP
loop

Quindi:

  • POLLIN accade quando l'input diventa disponibile
  • POLLHUP accade quando il file viene chiuso dal printf
  • close(pfd.fd); e pfd.fd *= -1; sistema le cose e smettiamo di ricevere POLLHUP
  • poll si blocca per sempre

Questa è l'operazione normale.

Ora puoi rispondere al FIFO per attendere il prossimo open o esci dal ciclo se hai finito.

POLLNALE

Se commenti pfd.fd *= -1; :./poll.out stampe:

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

e si ripete all'infinito.

Quindi:

  • POLLIN e POLLHUP e close successo come prima
  • poiché non abbiamo impostato pfd.fd a un numero negativo, poll continua a provare a usare il fd che abbiamo chiuso
  • questo continua a restituire POLLNVAL per sempre

Quindi vediamo che questo non sarebbe dovuto accadere e indica un bug nel tuo codice.

POLLERR

Non so come generare un POLLERR con FIFO. Fammi sapere se c'è modo. Ma dovrebbe essere possibile con file_operations di un driver di dispositivo.

Testato in Ubuntu 14.04.


Linux
  1. Come creare un alias e utilizzare il comando alias in Linux

  2. Come posso vedere la dimensione dei file e delle directory in Linux?

  3. Come posso spostare file e directory nella cartella principale in Linux?

  4. Come determinare il tempo di connessione del socket su Linux

  5. Come interpretare e correggere un errore di input/output in Linux?

Come cancellare (svuotare) la cache DNS su Windows, MacOS e Linux

Come usare e sfruttare al meglio il comando fusore in Linux

Come trovare ed elencare in modo ricorsivo i file per data in Linux

Come trovare il PID e il PPID di un processo in Linux

Come installare e utilizzare il comando Ack in Linux

Linux perf:come utilizzare il comando e il profiler