GNU/Linux >> Znalost Linux >  >> Linux

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

Mezi (p)select a (p)poll je poměrně jemný rozdíl:

Pro select musíte vždy před voláním select inicializovat a naplnit ošklivé bitmapy fd_set, protože select je „destruktivním“ způsobem upravuje na místě. (průzkum rozlišuje mezi .events a .revents členové v struct pollfd ).

Po výběru je celá bitmapa často skenována (lidmi/kódem) na události, i když většina fds není ani sledována.

Za třetí, bitmapa se dokáže vypořádat pouze s fds, jejichž počet je menší než určitý limit (současné implementace:někde mezi 1024..4096), což ji vylučuje v programech, kde lze snadno dosáhnout vysokých fds (nehledě na to, že takové programy pravděpodobně již místo toho použijte epoll).


Navrhoval bych začít srovnání s select() vs poll() . Linux také poskytuje obě pselect() a ppoll(); a navíc const sigset_t * argument na pselect() a ppoll() (vs select() a poll() ) má stejný účinek na každou "p-variantu". Pokud nepoužíváte signály, nemáte žádnou rasu, před kterou byste se mohli chránit, takže základní otázka je skutečně o efektivitě a snadném programování.

Mezitím zde již existuje odpověď na stackoverflow.com:jaké jsou rozdíly mezi hlasováním a výběrem.

Pokud jde o závod:jakmile začnete používat signály (z jakéhokoli důvodu), dozvíte se, že obecně by měl obsluha signálů nastavit proměnnou typu volatile sig_atomic_t k označení, že signál byl detekován. Základním důvodem je to, že mnoho hovorů z knihoven není opakováno a signál může být doručen, když jste „uprostřed“ takové rutiny. Například jednoduše vytiskněte zprávu do datové struktury ve stylu streamu, jako je stdout (C) nebo cout (C++) může vést k problémům s opětovným vstupem.

Předpokládejme, že máte kód, který používá volatile sig_atomic_t flag proměnná, možná k zachycení SIGINT , něco takového (viz také 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 ...
    ...

Nyní, v hlavním těle vašeho kódu, možná budete chtít "spustit do přerušení":

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

To je v pořádku, dokud nezačnete provádět volání, která čekají na nějaký vstup/výstup, například select nebo poll . Akce „čekání“ musí čekat na tento I/O – ale také musí počkat na SIGINT přerušit. Pokud stačí napsat:

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

pak je možné, že k přerušení dojde těsně předtím zavoláte select() nebo poll() , spíše než později. V tomto případě jste byli přerušeni – a proměnná got_interrupted se nastaví – ale poté začnete čekat. Měli jste zkontrolovat got_interrupted proměnná předtím, než jste začali čekat, ne poté.

Můžete zkusit napsat:

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

Tím se zmenší „okno závodu“, protože nyní zjistíte přerušení, pokud k němu dojde, když jste v kódu „udělejte nějakou práci“; ale stále probíhá závod, protože k přerušení může dojít hned poté otestujete proměnnou, ale těsně předtím výběr nebo hlasování.

Řešením je udělat sekvenci „test, pak čekej“ „atomickou“ pomocí vlastností blokování signálu sigprocmask (nebo ve vláknovém kódu 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 */
}

(Výše uvedený kód ve skutečnosti není tak skvělý, je strukturován spíše pro ilustraci než pro efektivitu – je efektivnější provést manipulaci s maskou signálu trochu jinak a testy „přerušeno“ umístit jinak).

Dokud skutečně nezačnete chytat SIGINT , ale stačí porovnat select() a poll() (a pokud začnete potřebovat velké množství deskriptorů, některé věci založené na událostech jako epoll() je efektivnější než kterýkoli z nich).


Linux
  1. Nelze přistupovat k vybraným webům Https v systému Linux přes Pppoe?

  2. Linuxový příkaz mv

  3. Linux du command

  1. Linuxový ip příkaz

  2. Linux cd příkaz

  3. 10 praktických příkladů příkazu Vyjmout v Linuxu pro výběr sloupců souboru

  1. Jak nainstalovat Erlang na Rocky Linux/Alma Linux/CentOS 8

  2. Linux – Jsou různá jádra Linux/unix zaměnitelná?

  3. Vyberte text pomocí klávesnice v linuxovém prostředí