GNU/Linux >> Linux Esercitazione >  >> Linux

Linux select() vs ppoll() vs pselect()

Tra (p)select e (p)poll c'è una differenza piuttosto sottile:

Per select, devi inizializzare e popolare le brutte bitmap fd_set ogni volta prima di chiamare select perché select le modifica sul posto in modo "distruttivo". (il sondaggio distingue tra .events e .revents membri in struct pollfd ).

Dopo la selezione, l'intera bitmap viene spesso scansionata (da persone/codice) alla ricerca di eventi anche se la maggior parte degli fds non viene nemmeno guardata.

In terzo luogo, la bitmap può gestire solo fd il cui numero è inferiore a un certo limite (implementazioni contemporanee:da qualche parte tra 1024..4096), il che lo esclude nei programmi in cui è possibile raggiungere facilmente fd elevati (nonostante che tali programmi possano usa già invece epoll).


Suggerirei di iniziare il confronto con select() contro poll() . Linux fornisce anche entrambi pselect() e ppoll(); e l'extra const sigset_t * argomento a pselect() e ppoll() (rispetto a select() e poll() ) ha lo stesso effetto su ciascuna "variante p", per così dire. Se non stai usando i segnali, non hai nessuna gara da cui proteggerti, quindi la domanda di base riguarda davvero l'efficienza e la facilità di programmazione.

Nel frattempo c'è già una risposta di stackoverflow.com qui:quali sono le differenze tra poll e select.

Per quanto riguarda la gara:una volta che inizi a utilizzare i segnali (per qualsiasi motivo), imparerai che in generale un gestore di segnali dovrebbe semplicemente impostare una variabile di tipo volatile sig_atomic_t per indicare che il segnale è stato rilevato. La ragione fondamentale di ciò è che molte chiamate in biblioteca non sono rientranti e un segnale può essere inviato mentre sei "nel mezzo" di una tale routine. Ad esempio, semplicemente stampando un messaggio in una struttura di dati in stile stream come stdout (C) o cout (C++) può portare a problemi di rientro.

Supponi di avere un codice che utilizza un volatile sig_atomic_t flag variabile, forse per catturare SIGINT , qualcosa del genere (vedi anche http://pubs.opengroup.org/onlinepubs/007904975/functions/sigaction.html):

volatile sig_atomic_t got_interrupted = 0;
void caught_signal(int unused) {
    got_interrupted = 1;
}
...
    struct sigaction sa;
    sa.sa_handler = caught_signal;
    sigemptyset(&sa.sa_mask);
    sa.sa_flags = SA_RESTART;
    if (sigaction(SIGINT, &sa, NULL) == -1) ... handle error ...
    ...

Ora, nel corpo principale del tuo codice, potresti voler "eseguire finché non viene interrotto":

    while (!got_interrupted) {
         ... do some work ...
    }

Questo va bene fino a quando non inizi a dover effettuare chiamate che attendono alcuni input/output, come select o poll . L'azione "wait" deve attendere quell'I/O, ma anche deve attendere un SIGINT interrompere. Se scrivi solo:

    while (!got_interrupted) {
        ... do some work ...
        result = select(...); /* or result = poll(...) */
    }

allora è possibile che l'interruzione si verifichi poco prima chiami select() o poll() , piuttosto che dopo. In questo caso, sei stato interrotto e la variabile got_interrupted viene impostato, ma dopo inizi ad aspettare. Avresti dovuto controllare il got_interrupted variabile prima che iniziassi ad aspettare, non dopo.

Puoi provare a scrivere:

    while (!got_interrupted) {
        ... do some work ...
        if (!got_interrupted)
            result = select(...); /* or result = poll(...) */
    }

Questo riduce la "finestra di gara", perché ora rileverai l'interruzione se si verifica mentre sei nel codice "fai un po' di lavoro"; ma c'è ancora una corsa, perché l'interruzione può avvenire subito dopo si verifica la variabile, ma subito prima seleziona-o-sondaggio.

La soluzione è rendere "atomica" la sequenza "test, then wait", utilizzando le proprietà di blocco del segnale di sigprocmask (o, nel codice thread POSIX, pthread_sigmask ):

sigset_t mask, omask;
...
while (!got_interrupted) {
    ... do some work ...
    /* begin critical section, test got_interrupted atomically */
    sigemptyset(&mask);
    sigaddset(&mask, SIGINT);
    if (sigprocmask(SIG_BLOCK, &mask, &omask))
        ... handle error ...
    if (got_interrupted) {
        sigprocmask(SIG_SETMASK, &omask, NULL); /* restore old signal mask */
        break;
    }
    result = pselect(..., &omask); /* or ppoll() etc */
    sigprocmask(SIG_SETMASK, &omask, NULL);
    /* end critical section */
}

(il codice sopra in realtà non è eccezionale, è strutturato per l'illustrazione piuttosto che per l'efficienza:è più efficiente eseguire la manipolazione della maschera del segnale in modo leggermente diverso e posizionare i test "è stato interrotto" in modo diverso).

Fino a quando non inizi effettivamente a dover catturare SIGINT , tuttavia, devi solo confrontare select() e poll() (e se inizi ad aver bisogno di un gran numero di descrittori, alcune delle cose basate sugli eventi come epoll() è più efficiente di entrambi).


Linux
  1. Come installare Erlang su Rocky Linux/Alma Linux/CentOS 8

  2. Non riesci ad accedere a determinati siti HTTP su Linux su Pppoe?

  3. Comando Linux mv

  4. Linux du comando

  5. 10 pratici esempi di comandi di taglio Linux per selezionare le colonne di file

Comando W in Linux

Al comando in Linux

Come configurare e utilizzare il client di posta elettronica Nylas N1 su Linux

Come creare un ebook con Calibre in Linux [Guida completa]

Linux vs Unix

Delimitatore CSV di WPS Office - Linux