Provided by: manpages-es_1.55-8_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.