bionic (2) select_tut.2.gz

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.