Provided by: manpages-es_1.55-10_all bug

NOMBRE

       select, pselect, FD_CLR, FD_ISSET, FD_SET, FD_ZERO - multiplexación de E/S síncrona

SINOPSIS

       #include <sys/time.h>
       #include <sys/types.h>
       #include <unistd.h>

       int  select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval
       *utimeout);

       int pselect(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,  const  struct
       timespec *ntimeout, sigset_t *sigmask);

       FD_CLR(int fd, fd_set *set);
       FD_ISSET(int fd, fd_set *set);
       FD_SET(int fd, fd_set *set);
       FD_ZERO(fd_set *set);

DESCRIPCIÓN

       select  (o  pselect) es la función eje de la mayor parte de programas en C que manejan más
       de un descriptor de fichero (o manejador de conector) simultáneamente de manera eficiente.
       Sus  principales argumentos son tres arrays de descriptores de fichero: readfds, writefds,
       y exceptfds. La forma de utilizar habitualmente select es bloquearse mientras se espera un
       "cambio  de estado" en uno o más de los descriptores de fichero.  Un "cambio de estado" se
       produce cuando se vuelven disponibles más carácteres del descriptor de fichero,  o  cuando
       se  dispone  de espacio en los buffers internos del núcleo para escribir más carácteres en
       el descriptor de fichero, o cuando un descriptor de fichero provoca un error (en  el  caso
       de un conector o tubería se da cuando se cierra el otro extremo de la conexión).

       En  resumen,  select  tan  sólo  vigila  varios  descriptores  de fichero, y es la llamada
       estándar en Unix para hacerlo.

       Los arrays de descriptores de fichero son llamados conjuntos de descriptores  de  fichero.
       Cada  conjunto  es declarado con el tipo fd_set, y su contenido puede ser alterado con las
       macros FD_CLR, FD_ISSET, FD_SET  y FD_ZERO. FD_ZERO es normalmente la primera función  que
       se  utiliza sobre un conjunto recién declarado. A partir de aquí, aquellos descriptores de
       fichero individuales en los que esté interesado  pueden  ser  añadidos  uno  por  uno  con
       FD_SET.   select  modifica el contenido de los conjuntos de acuerdo a las reglas descritas
       abajo; después de invocar a select puede comprobar si su descriptor de  fichero  está  aún
       presente  en  el  conjunto  con la macro FD_ISSET.  FD_ISSET devuelve un valor distinto de
       cero si el descriptor está presente y cero si no lo está. FD_CLR elimina un descriptor  de
       fichero del conjunto, aunque yo no veo el uso que puede tener en un programa correcto.

ARGUMENTOS

       readfds
              Este  conjunto  es  observado  para  ver  si  hay  datos  disponibles  para leer en
              cualquiera de sus descriptores de fichero. Después de que select  regrese,  borrará
              de  readfds  todos  los  descriptores de fichero salvo aquellos sobre los que pueda
              ejecutarse inmediatamente una operación de lectura con una llamada a  recv()  (para
              conectores) o read() (para tuberías, ficheros y conectores).

       writefds
              Este  conjunto  es  observado  para  ver  si  hay  espacio  para  escribir datos en
              cualquiera de sus descriptores de fichero. Después de que select  regrese,  borrará
              de writefds todos los descriptores de fichero salvo aquellos sobre los que se pueda
              ejecutar inmediatamente una operación de escritura con una llamada a  send()  (para
              conectores) o write() (para tuberías, ficheros y conectores).

       exceptfds
              Este  conjunto  es observado para las excepciones o errores sobre cualquiera de sus
              descriptores de fichero. Sin embargo, realmente es sólo un rumor. Para  lo  que  en
              verdad  usa  exceptfds  es para observar datos "fuera de orden" (OOB, out-of-band).
              Los datos OOB son datos enviados por un conector usando la bandera MSG_OOB,  y  por
              tanto  exceptfds sólo se aplica realmente a conectores. Vea el contenido de recv(2)
              y send(2) sobre este tema. Después de que  select  regrese,  borrará  de  exceptfds
              todos  los descriptores de fichero salvo aquellos sobre los que se puede leer datos
              OOB. Sólo puede leer un byte de datos  OOB  de  todas  maneras  (con  la  operación
              recv()),  y  se  pueden escribir datos OOB en cualquier momento sin bloquearse. Por
              tanto no hay necesidad de un cuarto conjunto para comprobar si en un  conector  hay
              disponibles datos OOB para escribir.

       nfds   Es  un  entero  que indica uno más del máximo de cualquier descriptor de fichero en
              cualquiera de los conjuntos. En otras palabras, mientras  está  atareado  añadiendo
              descriptores  de  fichero  a sus conjuntos, debe calcular el máximo valor entero de
              todos ellos, incrementar este valor en uno, y pasarlo como nfds a select.

       utimeout
              Es el máximo valor de tiempo que select debe esperar antes de regresar, incluso  si
              nada  interesante  ocurrió.  Si  este  valor se pasa como NULL, select se bloqueará
              indefinidamente esperando un evento.  utimeout puede ser puesto a cero segundos, lo
              que  provoca  que  select regrese inmediatamente. La estructura struct timeval está
              definida como,

              struct timeval {
                  time_t tv_sec;    /* segundos */
                  long tv_usec;     /* microsegundos */
              };

       ntimeout
              Este argumento tiene el mismo significado que utimeout pero struct  timespec  tiene
              precisión de nanosegundos como sigue,

              struct timespec {
                  long tv_sec;    /* segundos */
                  long tv_nsec;   /* nanosegundos */
              };

       sigmask
              Este  argumento  contiene un conjunto de señales permitidas mientras se realiza una
              llamada a pselect (vea sigaddset(3) y sigprocmask(2)). Puede valer  NULL,  en  cuyo
              caso  no modifica el conjunto de señales permitidas en la entrada y la salida de la
              función. Se comportará igual que select.

COMBINANDO SEÑALES Y EVENTOS DE DATOS

       pselect debe ser usada si está esperando una señal así como  datos  de  un  descriptor  de
       fichero.  Los programas que reciben señales como eventos normalmente utilizan el manejador
       de señales para activar una bandera global.  La bandera global indicará que el evento debe
       ser  procesado  en  el  bucle principal del programa. Una señal provocará que la llamada a
       select (o pselect) regrese tomando la variable errno el valor EINTR.  Este  comportamiento
       es esencial para que las señales puedan ser procesadas en el bucle principal del programa,
       de otra manera select se bloquearía indefinidamente.  Ahora,  en  algún  lugar  del  bucle
       principal  habrá  una  condición  para  comprobar  la  bandera  global.  Así  que  debemos
       preguntarnos: ¿qué ocurre si una señal llega después de la condición,  pero  antes  de  la
       llamada  a  select?  La respuesta es que select se bloquearía indefinidamente, incluso aún
       si hay un evento pendiente. Esta condición de carrera se soluciona con la llamada pselect.
       Esta  llamada  puede  utilizarse  para enmascarar señales que no van a ser recibidas salvo
       dentro de la llamada pselect. Por ejemplo, digamos que el evento en cuestión fue la salida
       de  un  proceso hijo. Antes del comienzo del bucle principal, bloquearíamos SIGCHLD usando
       sigprocmask. Nuestra llamada pselect podría habilitar SIGCHLD usando la máscara  de  señal
       virgen. Nuestro programa se podría parecer a ésto:

       int child_events = 0;

       void child_sig_handler (int x) {
           child_events++;
           signal (SIGCHLD, child_sig_handler);
       }

       int main (int argc, char **argv) {
           sigset_t sigmask, orig_sigmask;

           sigemptyset (&sigmask);
           sigaddset (&sigmask, SIGCHLD);
           sigprocmask (SIG_BLOCK, &sigmask,
                                       &orig_sigmask);

           signal (SIGCHLD, child_sig_handler);

           for (;;) { /* bucle principal */
               for (; child_events > 0; child_events--) {
                   /* procesar el evento aquí */
               }
               r = pselect (nfds, &rd, &wr, &er, 0, &orig_sigmask);

               /* cuerpo principal del programa */
           }
       }

       Observe que la llamada pselect de arriba puede ser reemplazada con:

               sigprocmask (SIG_BLOCK, &orig_sigmask, 0);
               r = select (nfds, &rd, &wr, &er, 0);
               sigprocmask (SIG_BLOCK, &sigmask, 0);

       pero  todavía  queda  la  posibilidad  de  que  una  señal pueda llegar después del primer
       sigprocmask y antes de select. Si hace esto, es prudente que ponga al menos un  tiempo  de
       espera  finito  para  que  el  proceso  no  se  bloquee.   Es  probable que glibc funcione
       actualmente de esta manera. El núcleo de Linux no tiene todavía  una  llamada  al  sistema
       pselect nativa por lo que probablemente todo esto sea nada más que hablar por hablar.

PRÁCTICA

       Por  lo  tanto,  ¿cuál es el propósito de select? ¿No puedo simplemente leer y escribir en
       mis descriptores  siempre  que  quiera?  El  significado  de  select  es  observar  varios
       descriptores  al  mismo  tiempo  y  poner  a dormir adecuadamente a los procesos si no hay
       ninguna actividad. Esto lo hace mientras le permite manejar varias tuberías  y  conectores
       de  manera simultánea. Los programadores de Unix a menudo se encuentran en la situación de
       manejar la E/S de más de un descriptor de fichero  donde  el  flujo  de  datos  puede  ser
       intermitente.   Si  tan  sólo  creara  una  secuencia  de  llamadas  read  y write, podría
       encontrarse con que  una  de  sus  llamadas  puede  bloquearse  esperando  datos  de/a  un
       descriptor  de  fichero,  mientras  que otro descriptor de fichero está siendo inutilizado
       aunque haya datos disponibles.  select maneja eficientemente esta situación.

       Un ejemplo típico de select lo podemos encontrar en la página de manual de select:

       #include <stdio.h>
       #include <sys/time.h>
       #include <sys/types.h>
       #include <unistd.h>

       int
       main(void) {
           fd_set rfds;
           struct timeval tv;
           int retval;

           /* Observar stdin (descriptor 0) para ver cuando hay
              entrada disponible. */
           FD_ZERO(&rfds);
           FD_SET(0, &rfds);
           /* Esperar hasta cinco segundos. */
           tv.tv_sec = 5;
           tv.tv_usec = 0;

           retval = select(1, &rfds, NULL, NULL, &tv);
           /* No confíe en el valor de tv por ahora! */

           if (retval)
               printf("Los datos ya están disponibles.\n");
               /* FD_ISSET(0, &rfds) será verdadero. */
           else
               printf("No ha habido datos en cinco segundos.\n");

           exit(0);
       }

EJEMPLO DE REDIRECCIÓN DE PUERTOS

       Aquí viene un ejemplo que ilustra mejor la verdadera utilidad de  select.  El  listado  de
       abajo es un programa de reenvío TCP que redirige de un puerto TCP a otro.

       #include <stdlib.h>
       #include <stdio.h>
       #include <unistd.h>
       #include <sys/time.h>
       #include <sys/types.h>
       #include <string.h>
       #include <signal.h>
       #include <sys/socket.h>
       #include <netinet/in.h>
       #include <arpa/inet.h>
       #include <errno.h>

       static int forward_port;

       #undef max
       #define max(x,y) ((x) > (y) ? (x) : (y))

       static int listen_socket (int listen_port) {
           struct sockaddr_in a;
           int s;
           int yes;
           if ((s = socket (AF_INET, SOCK_STREAM, 0)) < 0) {
               perror ("socket");
               return -1;
           }
           yes = 1;
           if (setsockopt
               (s, SOL_SOCKET, SO_REUSEADDR,
                (char *) &yes, sizeof (yes)) < 0) {
               perror ("setsockopt");
               close (s);
               return -1;
           }
           memset (&a, 0, sizeof (a));
           a.sin_port = htons (listen_port);
           a.sin_family = AF_INET;
           if (bind
               (s, (struct sockaddr *) &a, sizeof (a)) < 0) {
               perror ("bind");
               close (s);
               return -1;
           }
           printf ("aceptando conexiones en el puerto %d\n",
                   (int) listen_port);
           listen (s, 10);
           return s;
       }

       static int connect_socket (int connect_port,
                                  char *address) {
           struct sockaddr_in a;
           int s;
           if ((s = socket (AF_INET, SOCK_STREAM, 0)) < 0) {
               perror ("socket");
               close (s);
               return -1;
           }

           memset (&a, 0, sizeof (a));
           a.sin_port = htons (connect_port);
           a.sin_family = AF_INET;

           if (!inet_aton
               (address,
                (struct in_addr *) &a.sin_addr.s_addr)) {
               perror ("formato de dirección IP incorrecto");
               close (s);
               return -1;
           }

           if (connect
               (s, (struct sockaddr *) &a,
                sizeof (a)) < 0) {
               perror ("connect()");
               shutdown (s, SHUT_RDWR);
               close (s);
               return -1;
           }
           return s;
       }

       #define SHUT_FD1 {                      \
               if (fd1 >= 0) {                 \
                   shutdown (fd1, SHUT_RDWR);  \
                   close (fd1);                \
                   fd1 = -1;                   \
               }                               \
           }

       #define SHUT_FD2 {                      \
               if (fd2 >= 0) {                 \
                   shutdown (fd2, SHUT_RDWR);  \
                   close (fd2);                \
                   fd2 = -1;                   \
               }                               \
           }

       #define BUF_SIZE 1024

       int main (int argc, char **argv) {
           int h;
           int fd1 = -1, fd2 = -1;
           char buf1[BUF_SIZE], buf2[BUF_SIZE];
           int buf1_avail, buf1_written;
           int buf2_avail, buf2_written;

           if (argc != 4) {
               fprintf (stderr,
                        "Uso\n\tfwd <puerto-escucha> \
       <redirigir-a-puerto> <redirigir-a-dirección-ip>\n");
               exit (1);
           }

           signal (SIGPIPE, SIG_IGN);

           forward_port = atoi (argv[2]);

           h = listen_socket (atoi (argv[1]));
           if (h < 0)
               exit (1);

           for (;;) {
               int r, nfds = 0;
               fd_set rd, wr, er;
               FD_ZERO (&rd);
               FD_ZERO (&wr);
               FD_ZERO (&er);
               FD_SET (h, &rd);
               nfds = max (nfds, h);
               if (fd1 > 0 && buf1_avail < BUF_SIZE) {
                   FD_SET (fd1, &rd);
                   nfds = max (nfds, fd1);
               }
               if (fd2 > 0 && buf2_avail < BUF_SIZE) {
                   FD_SET (fd2, &rd);
                   nfds = max (nfds, fd2);
               }
               if (fd1 > 0
                   && buf2_avail - buf2_written > 0) {
                   FD_SET (fd1, &wr);
                   nfds = max (nfds, fd1);
               }
               if (fd2 > 0
                   && buf1_avail - buf1_written > 0) {
                   FD_SET (fd2, &wr);
                   nfds = max (nfds, fd2);
               }
               if (fd1 > 0) {
                   FD_SET (fd1, &er);
                   nfds = max (nfds, fd1);
               }
               if (fd2 > 0) {
                   FD_SET (fd2, &er);
                   nfds = max (nfds, fd2);
               }

               r = select (nfds + 1, &rd, &wr, &er, NULL);

               if (r == -1 && errno == EINTR)
                   continue;
               if (r < 0) {
                   perror ("select()");
                   exit (1);
               }
               if (FD_ISSET (h, &rd)) {
                   unsigned int l;
                   struct sockaddr_in client_address;
                   memset (&client_address, 0, l =
                           sizeof (client_address));
                   r = accept (h, (struct sockaddr *)
                               &client_address, &l);
                   if (r < 0) {
                       perror ("accept()");
                   } else {
                       SHUT_FD1;
                       SHUT_FD2;
                       buf1_avail = buf1_written = 0;
                       buf2_avail = buf2_written = 0;
                       fd1 = r;
                       fd2 =
                           connect_socket (forward_port,
                                           argv[3]);
                       if (fd2 < 0) {
                           SHUT_FD1;
                       } else
                           printf ("conexión desde %s\n",
                                   inet_ntoa
                                   (client_address.sin_addr));
                   }
               }
       /* NB: lee datos OOB antes de las lecturas normales */
               if (fd1 > 0)
                   if (FD_ISSET (fd1, &er)) {
                       char c;
                       errno = 0;
                       r = recv (fd1, &c, 1, MSG_OOB);
                       if (r < 1) {
                           SHUT_FD1;
                       } else
                           send (fd2, &c, 1, MSG_OOB);
                   }
               if (fd2 > 0)
                   if (FD_ISSET (fd2, &er)) {
                       char c;
                       errno = 0;
                       r = recv (fd2, &c, 1, MSG_OOB);
                       if (r < 1) {
                           SHUT_FD1;
                       } else
                           send (fd1, &c, 1, MSG_OOB);
                   }
               if (fd1 > 0)
                   if (FD_ISSET (fd1, &rd)) {
                       r =
                           read (fd1, buf1 + buf1_avail,
                                 BUF_SIZE - buf1_avail);
                       if (r < 1) {
                           SHUT_FD1;
                       } else
                           buf1_avail += r;
                   }
               if (fd2 > 0)
                   if (FD_ISSET (fd2, &rd)) {
                       r =
                           read (fd2, buf2 + buf2_avail,
                                 BUF_SIZE - buf2_avail);
                       if (r < 1) {
                           SHUT_FD2;
                       } else
                           buf2_avail += r;
                   }
               if (fd1 > 0)
                   if (FD_ISSET (fd1, &wr)) {
                       r =
                           write (fd1,
                                  buf2 + buf2_written,
                                  buf2_avail -
                                  buf2_written);
                       if (r < 1) {
                           SHUT_FD1;
                       } else
                           buf2_written += r;
                   }
               if (fd2 > 0)
                   if (FD_ISSET (fd2, &wr)) {
                       r =
                           write (fd2,
                                  buf1 + buf1_written,
                                  buf1_avail -
                                  buf1_written);
                       if (r < 1) {
                           SHUT_FD2;
                       } else
                           buf1_written += r;
                   }
       /* comprueba si se han escrito tantos datos como se han leído */
               if (buf1_written == buf1_avail)
                   buf1_written = buf1_avail = 0;
               if (buf2_written == buf2_avail)
                   buf2_written = buf2_avail = 0;
       /* si un extremo ha cerrado la conexión, continúa escribiendo al otro
          extremo hasta que no queden datos */
               if (fd1 < 0
                   && buf1_avail - buf1_written == 0) {
                   SHUT_FD2;
               }
               if (fd2 < 0
                   && buf2_avail - buf2_written == 0) {
                   SHUT_FD1;
               }
           }
           return 0;
       }

       El  programa  anterior  reenvía  correctamente  la mayoría de los tipos de conexiones TCP,
       incluyendo los datos OOB de señal transmitidos por los servidores telnet. También es capaz
       de  manejar  el  difícil  problema de tener flujos de datos en ambas direcciones a la vez.
       Podría pensar que es más eficiente hacer una llamada fork()  y  dedicar  un  hilo  a  cada
       flujo.  Esto  es  más  complicado  de  lo  que  podría pensar. Otra idea es activar E/S no
       bloqueante haciendo una llamada ioctl(). Esto también tiene sus  problemas  ya  que  acaba
       teniendo que utilizar plazos de tiempo (timeouts) ineficientes.

       El  programa  no  maneja más de una conexión simultánea a la vez, aunque podría extenderse
       fácilmente para hacer esto con una lista ligada de buffers - uno para cada  conexión.  Por
       ahora, una nueva conexión hace que la conexión actual se caiga.

REGLAS DE SELECT

       Muchas  personas  que  intentan  usar  select  se  encuentran con un comportamiento que es
       difícil de comprender y que produce resultados no transportables o dudosos.  Por  ejemplo,
       el  programa anterior se ha escrito cuidadosamente para que no se bloquee en ningún punto,
       aunque para nada ha establecido el modo no bloqueante en sus descriptores de fichero  (vea
       ioctl(2)).  Es  fácil  introducir errores sutiles que hagan desaparecer la ventaja de usar
       select, por lo que voy a presentar una lista de los aspectos esenciales a tener en  cuenta
       cuando se use la llamada select.

       1.     Siempre  debe  de  intentar usar select sin un plazo de tiempo. Su programa no debe
              tener que hacer nada si no hay datos disponibles. El  código  que  depende  de  los
              plazos de tiempo no es normalmente portable y es difícil de depurar.

       2.     Para  un resultado eficiente, el valor de nfds se debe calcular correctamente de la
              forma que se explica más abajo.

       3.     No debe añadir a ningún conjunto un descriptor de fichero  para  el  que  no  tenga
              intención  de comprobar su resultado (y responder adecuadamente) tras una llamada a
              select. Vea la siguiente regla.

       4.     Cuando select regrese, se deben comprobar todos  los  descriptores  de  fichero  de
              todos  los  conjuntos. Se debe escribir en cualquier descriptor de fichero que esté
              listo para ello, se debe leer de cualquier descriptor de  fichero  que  esté  listo
              para ello, etc.

       5.     Las  funciones  read(),  recv(),  write()  y send() no leen/escriben necesariamente
              todos los datos que haya solicitado. Si leen/escriben todos  los  datos  es  porque
              tiene  poco  tráfico  y  un flujo muy rápido. Ese no va a ser siempre el caso. Debe
              hacer frente al caso en el que sus funciones sólo logren enviar o recibir un  único
              byte.

       6.     Nunca  lea/escriba  byte  a byte a menos que esté realmente seguro de que tiene que
              procesar  una  pequeña  cantidad  de  datos.  Es  extremadamente   ineficiente   no
              leer/escribir  cada  vez tantos datos como pueda almacenar. Los buffers del ejemplo
              anterior son de 1024 bytes aunque podrían fácilmente hacerse tan  grandes  como  el
              máximo tamaño de paquete posible en su red local.

       7.     Además  de  la  llamada  select(),  las  funciones read(), recv(), write() y send()
              pueden devolver -1 con un errno EINTR o EAGAIN (EWOULDBLOCK) que  no  son  errores.
              Estos  resultados deben tratarse adecuadamente (lo que no se ha hecho en el ejemplo
              anterior). Si su programa no va a recibir  ninguna  señal,  entonces  es  muy  poco
              probable  que  obtenga  EINTR.   Si  su  programa  no  activa E/S no bloqueante, no
              obtendrá EAGAIN.  Sin embargo, todavía  debe  hacer  frente  a  estos  errores  por
              completitud.

       8.     Nunca llame a read(), recv(), write() o send() con una longitud de buffer de cero.

       9.     Excepto  como se indica en 7., las funciones read(), recv(), write() y send() nunca
              devuelven un valor menor que 1 salvo cuando se produce un error.  Por  ejemplo,  un
              read()  sobre  una  tubería donde el otro extremo ha muerto devuelve cero (al igual
              que un error de fin de fichero), pero devuelve cero sólo  una  vez  (un  lectura  o
              escritura  posterior devolverá -1). Cuando cualquiera de estas funciones devuelva 0
              o -1, no debe pasar el descriptor correspondiente a select nunca más. En el ejemplo
              anterior,  cierro  el  descriptor  inmediatamente y le asigno -1 para evitar que se
              vuelva a incluir en un conjunto.

       10.    El valor del plazo de tiempo debe inicializarse con cada nueva llamada a select, ya
              que  algunos  sistemas operativos modifican la estructura. pselect, sin embargo, no
              modifica su estructura de plazo de tiempo.

       11.    He oído que la capa de conectores de Windows no sabe tratar adecuadamente los datos
              OOB.  Tampoco sabe tratar llamadas select cuando ningún descriptor de fichero se ha
              incluido en ningún conjunto. No tener ningún descriptor de fichero  activo  es  una
              forma útil de domir a un proceso con una precisión de menos de un segundo usando el
              plazo de tiempo. (Mire más abajo.)

EMULACIÓN DE USLEEP

       En sistemas que no tienen una función usleep, puede llamar a select con un plazo de espera
       finito y sin descriptores de fichero de la siguiente  manera:

           struct timeval tv;
           tv.tv_sec = 0;
           tv.tv_usec = 200000;  /* 0.2 segundos */
           select (0, NULL, NULL, NULL, &tv);

       Sin embargo, sólo se garantiza que funcionará en sistemas Unix.

VALOR DEVUELTO

       En  caso  de  éxito,  select  devuelve el número total de descriptores que están presentes
       todavía en los conjuntos de descriptores de fichero.

       Si se cumple el plazo de espera para select, los  conjuntos  de  descriptores  de  fichero
       deberían  estar  vacíos  (pero  en algunos sistemas puede que no sea así).  Sin embargo el
       valor devuelto será definitivamente cero.

       Un  valor  devuelto  de  -1  indica  un  error,  y  la  variable  errno  será   modificada
       apropiadamente.  En caso de error, el contenido de los conjuntos devueltos y la estructura
       timeout es indefinido y no debería ser usado.  pselect, sin  embargo,  no  modifica  nunca
       ntimeout.

ERRORES

       EBADF  Un conjunto contiene un descriptor de fichero no válido. Este error ocurre a menudo
              cuando añade a un conjunto un descriptor de fichero sobre el que ya se ha ejecutado
              la  operación  close, o cuando ese descriptor de fichero ya ha experimentado alguna
              clase de error. Por tanto  debería  dejar  de  añadir  a  los  conjuntos  cualquier
              descriptor de fichero que devuelva un error de lectura o escritura.

       EINTR  Una  señal  de interrupción fue capturada, como SIGINT o SIGCHLD etc.  En este caso
              debería reconstruir sus conjuntos de descriptores de fichero y volverlo a intentar.

       EINVAL Ocurre si nfds es negativo o si se especifica un valor incorrecto para  utimeout  o
              ntimeout.

       ENOMEM Fallo interno de reserva de memoria.

OBSERVACIONES

       Generalmente  hablando,  todos  los  sistemas  operativos que soportan conectores, también
       soportan select. Algunas personas  consideran  que  select  es  una  función  esotérica  y
       raramente usada. De hecho, muchos tipos de programas se vuelven extremadamente complicados
       sin ella. select puede utilizarse para solucionar muchos problemas de manera  eficiente  y
       portable.  Problemas  que  los  programadores  ingenuos  tratan  de resolver usando hilos,
       procesos hijos, IPCs, señales, memoria compartida y otros oscuros métodos. pselect es  una
       función más reciente que es menos comúnmente usada.

       La  llamada  al  sistema  poll(2)  tiene  la  misma  funcionalidad que select, pero con un
       comportamiento menos sutil. Es menos portable que select.

CONFORME A

       4.4BSD (la función select apareció por  primera  vez  en  4.2BSD).  Generalmente  portable
       a/desde  sistemas  no-BSD  que  soporten  clones  de  la  capa de conector BSD (incluyendo
       variantes de System V). Sin embargo,  observe  que  la  variante  de  System  V  establece
       normalmente  la  variable  timeout  antes  de salir, mientras que la variante de BSD no lo
       hace.

       La función pselect está definida en IEEE Std 1003.1g-2000  (POSIX.1g).   Se  encuentra  en
       glibc2.1  en  adelante. Glibc2.0 tiene una función con el mismo nombre, que sin embargo no
       acepta un parámetro sigmask.

VÉASE TAMBIÉN

       accept(2),  connect(2),  ioctl(2),  poll(2),   read(2),   recv(2),   select(2),   send(2),
       sigaddset(3), sigdelset(3), sigemptyset(3), sigfillset(3), sigismember(3), sigprocmask(2),
       write(2)

AUTORES

       Esta página de manual fue escrita por Paul Sheer.