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.