GNU/Linux >> Tutoriales Linux >  >> Linux

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

Entre (p)select y (p)poll hay una diferencia bastante sutil:

Para seleccionar, debe inicializar y completar los feos mapas de bits fd_set cada vez antes de llamar a seleccionar porque seleccionar los modifica en el lugar de una manera "destructiva". (la encuesta distingue entre el .events y .revents miembros en struct pollfd ).

Después de seleccionar, a menudo se escanea todo el mapa de bits (por personas/código) en busca de eventos, incluso si la mayoría de los fds ni siquiera se ven.

En tercer lugar, el mapa de bits solo puede tratar con fds cuyo número es inferior a un cierto límite (implementaciones contemporáneas:en algún lugar entre 1024 y 4096), lo que lo descarta en programas en los que se pueden alcanzar fácilmente fds altos (a pesar de que es probable que tales programas ya usa epoll en su lugar).


Sugeriría comenzar la comparación con select() contra poll() . Linux también proporciona tanto pselect() y ppoll(); y el extra const sigset_t * argumento para pselect() y ppoll() (frente a select() y poll() ) tiene el mismo efecto en cada "p-variante", por así decirlo. Si no está utilizando señales, no tiene una carrera contra la que protegerse, por lo que la pregunta básica es realmente sobre la eficiencia y la facilidad de programación.

Mientras tanto, ya hay una respuesta de stackoverflow.com aquí:¿cuáles son las diferencias entre sondear y seleccionar?

En cuanto a la carrera:una vez que comience a usar señales (por cualquier motivo), aprenderá que, en general, un controlador de señales debe establecer una variable de tipo volatile sig_atomic_t para indicar que la señal ha sido detectada. La razón fundamental de esto es que muchas llamadas de la biblioteca no vuelven a entrar, y se puede enviar una señal mientras está "en medio" de dicha rutina. Por ejemplo, simplemente imprimiendo un mensaje en una estructura de datos de estilo flujo como stdout (C) o cout (C++) puede provocar problemas de reingreso.

Suponga que tiene un código que usa un volatile sig_atomic_t flag variable, tal vez para capturar SIGINT , algo como esto (ver también 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 ...
    ...

Ahora, en el cuerpo principal de su código, es posible que desee "ejecutar hasta que se interrumpa":

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

Esto está bien hasta que empiece a necesitar hacer llamadas que esperan alguna entrada/salida, como select o poll . La acción "esperar" debe esperar esa E/S, pero también necesita esperar un SIGINT interrumpir. Si solo escribes:

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

entonces es posible que la interrupción suceda justo antes llamas al select() o poll() , en lugar de después. En este caso, te interrumpieron y la variable got_interrupted se establece, pero después de eso, comienza a esperar. Deberías haber marcado el got_interrupted variable antes de empezar a esperar, no después.

Puedes intentar escribir:

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

Esto reduce la "ventana de carrera", porque ahora detectará la interrupción si ocurre mientras está en el código "hacer algo de trabajo"; pero todavía hay una carrera, porque la interrupción puede ocurrir justo después prueba la variable, pero justo antes seleccionar o sondear.

La solución es hacer que la secuencia "probar, luego esperar" sea "atómica", usando las propiedades de bloqueo de señal de sigprocmask (o, en código roscado 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 */
}

(El código anterior en realidad no es tan bueno, está estructurado para ilustración en lugar de eficiencia:es más eficiente hacer la manipulación de la máscara de señal de manera ligeramente diferente y colocar las pruebas "interrumpidas" de manera diferente).

Hasta que realmente empieces a necesitar atrapar SIGINT , sin embargo, solo necesita comparar select() y poll() (y si comienza a necesitar una gran cantidad de descriptores, algunas de las cosas basadas en eventos como epoll() es más eficiente que cualquiera de los dos).


Linux
  1. ¿No puede acceder a sitios Https seleccionados en Linux a través de Pppoe?

  2. Linux:¿los diferentes kernels de Linux/unix son intercambiables?

  3. Comando IP de Linux

  4. Comando cd de linux

  5. 10 ejemplos prácticos de comandos de corte de Linux para seleccionar columnas de archivos

Comando W en Linux

Al mando en Linux

Cómo configurar y usar el cliente de correo electrónico Nylas N1 en Linux

Cómo crear un libro electrónico con Calibre en Linux [Guía completa]

Linux frente a Unix

Delimitador CSV de WPS Office - Linux