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

NOMBRE

       select,  pselect, FD_CLR, FD_ISSET, FD_SET, FD_ZERO - multiplexacion de
       E/S sincrona

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'ON

       select (o pselect) es la funcion eje de la mayor parte de programas  en
       C que manejan mas de un descriptor de fichero (o manejador de conector)
       simultaneamente 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 mas de los descriptores de
       fichero.   Un  "cambio  de  estado"  se  produce  cuando   se   vuelven
       disponibles  mas  caracteres  del  descriptor  de  fichero, o cuando se
       dispone de espacio en los buffers internos del nucleo para escribir mas
       caracteres  en  el  descriptor  de  fichero,  o cuando un descriptor de
       fichero provoca un error (en el caso de un conector  o  tuberia  se  da
       cuando se cierra el otro extremo de la conexion).

       En resumen, select tan solo vigila varios descriptores de fichero, y es
       la llamada estandar 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 funcion
       que se utiliza sobre un conjunto recien declarado. A  partir  de  aqui,
       aquellos   descriptores   de  fichero  individuales  en  los  que  este
       interesado pueden ser anadidos uno por uno con FD_SET.  select modifica
       el  contenido de los conjuntos de acuerdo a las reglas descritas abajo;
       despues de invocar a select puede comprobar si su descriptor de fichero
       esta  aun  presente  en  el  conjunto  con la macro FD_ISSET.  FD_ISSET
       devuelve un valor distinto de cero si el  descriptor  esta  presente  y
       cero  si  no  lo  esta.  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.  Despues
              de que select regrese, borrara de readfds todos los descriptores
              de  fichero  salvo  aquellos  sobre  los  que  pueda  ejecutarse
              inmediatamente una operacion de lectura con una llamada a recv()
              (para  conectores)  o  read()   (para   tuberias,   ficheros   y
              conectores).

       writefds
              Este conjunto es observado para ver si hay espacio para escribir
              datos en cualquiera de sus descriptores de fichero.  Despues  de
              que  select  regrese, borrara de writefds todos los descriptores
              de fichero salvo  aquellos  sobre  los  que  se  pueda  ejecutar
              inmediatamente  una  operacion  de  escritura  con una llamada a
              send() (para conectores) o write() (para  tuberias,  ficheros  y
              conectores).

       exceptfds
              Este  conjunto es observado para las excepciones o errores sobre
              cualquiera  de  sus  descriptores  de  fichero.   Sin   embargo,
              realmente  es solo 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  solo  se  aplica  realmente  a
              conectores.  Vea  el  contenido  de recv(2) y send(2) sobre este
              tema. Despues de que select regrese, borrara de exceptfds  todos
              los  descriptores  de  fichero  salvo  aquellos sobre los que se
              puede leer datos OOB. Solo puede leer un byte de  datos  OOB  de
              todas  maneras  (con  la operacion 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  mas  del  maximo  de  cualquier
              descriptor  de  fichero en cualquiera de los conjuntos. En otras
              palabras,  mientras  esta  atareado  anadiendo  descriptores  de
              fichero a sus conjuntos, debe calcular el maximo valor entero de
              todos ellos, incrementar este valor en uno, y pasarlo como  nfds
              a select.

       utimeout
              Es  el  maximo  valor de tiempo que select debe esperar antes de
              regresar, incluso si nada interesante ocurrio. Si este valor  se
              pasa como NULL, select se bloqueara indefinidamente esperando un
              evento.  utimeout puede ser  puesto  a  cero  segundos,  lo  que
              provoca  que select regrese inmediatamente. La estructura struct
              timeval esta 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 precision de nanosegundos como sigue,

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

       sigmask
              Este  argumento  contiene  un  conjunto  de  senales  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 senales permitidas en la entrada y la salida  de  la
              funcion. Se comportara igual que select.

COMBINANDO SE~NALES Y EVENTOS DE DATOS

       pselect debe ser usada si esta esperando una senal asi como datos de un
       descriptor de fichero. Los programas que reciben senales  como  eventos
       normalmente  utilizan  el manejador de senales para activar una bandera
       global.  La bandera global indicara que el evento debe ser procesado en
       el  bucle  principal del programa. Una senal provocara que la llamada a
       select (o pselect) regrese tomando la variable errno  el  valor  EINTR.
       Este  comportamiento  es  esencial  para  que  las  senales  puedan ser
       procesadas en el bucle principal del programa, de otra manera select se
       bloquearia  indefinidamente.  Ahora, en algun lugar del bucle principal
       habra una condicion para comprobar la bandera global. Asi  que  debemos
       preguntarnos:  cque  ocurre si una senal llega despues de la condicion,
       pero antes de la llamada a select?   La  respuesta  es  que  select  se
       bloquearia  indefinidamente,  incluso  aun  si hay un evento pendiente.
       Esta condicion de carrera se soluciona con  la  llamada  pselect.  Esta
       llamada  puede  utilizarse  para  enmascarar  senales  que no van a ser
       recibidas salvo dentro de la llamada pselect. Por ejemplo, digamos  que
       el  evento  en  cuestion  fue  la  salida de un proceso hijo. Antes del
       comienzo del bucle principal, bloqueariamos SIGCHLD usando sigprocmask.
       Nuestra  llamada  pselect podria habilitar SIGCHLD usando la mascara de
       senal virgen. Nuestro programa se podria parecer a esto:

       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 aqui */
               }
               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 todavia queda la posibilidad de que una senal pueda llegar despues
       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
       nucleo de Linux no tiene todavia una llamada al sistema pselect  nativa
       por lo que probablemente todo esto sea nada mas que hablar por hablar.

PR'ACTICA

       Por  lo  tanto,  ccual es el proposito de select? cNo 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 tuberias y conectores de
       manera simultanea. Los programadores de Unix a menudo se encuentran  en
       la situacion de manejar la E/S de mas de un descriptor de fichero donde
       el flujo de datos puede ser  intermitente.   Si  tan  solo  creara  una
       secuencia  de  llamadas read y write, podria encontrarse con que una de
       sus llamadas puede bloquearse esperando datos  de/a  un  descriptor  de
       fichero,   mientras   que   otro  descriptor  de  fichero  esta  siendo
       inutilizado   aunque   haya   datos   disponibles.     select    maneja
       eficientemente esta situacion.

       Un ejemplo tipico de select lo podemos encontrar en la pagina 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 confie en el valor de tv por ahora! */

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

           exit(0);
       }

EJEMPLO DE REDIRECCI'ON DE PUERTOS

       Aqui viene un ejemplo  que  ilustra  mejor  la  verdadera  utilidad  de
       select.  El listado de abajo es un programa de reenvio 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 direccion 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-direccion-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 ("conexion 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 leido */
               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 conexion, continua 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 reenvia correctamente la mayoria de los  tipos  de
       conexiones  TCP, incluyendo los datos OOB de senal transmitidos por los
       servidores telnet. Tambien es capaz de manejar el dificil  problema  de
       tener flujos de datos en ambas direcciones a la vez.  Podria pensar que
       es mas eficiente hacer una llamada fork() y  dedicar  un  hilo  a  cada
       flujo.  Esto  es  mas  complicado de lo que podria pensar. Otra idea es
       activar E/S no bloqueante haciendo una llamada  ioctl().  Esto  tambien
       tiene sus problemas ya que acaba teniendo que utilizar plazos de tiempo
       (timeouts) ineficientes.

       El programa no maneja mas de una conexion simultanea a la  vez,  aunque
       podria  extenderse  facilmente  para hacer esto con una lista ligada de
       buffers - uno para cada conexion. Por ahora, una  nueva  conexion  hace
       que la conexion actual se caiga.

REGLAS DE SELECT

       Muchas   personas  que  intentan  usar  select  se  encuentran  con  un
       comportamiento que es dificil 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 ningun  punto,  aunque
       para  nada  ha establecido el modo no bloqueante en sus descriptores de
       fichero (vea ioctl(2)). Es facil 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 codigo que depende de los plazos de tiempo no es
              normalmente portable y es dificil de depurar.

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

       3.     No  debe  anadir a ningun conjunto un descriptor de fichero para
              el que no tenga intencion 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 este listo para ello, se debe leer  de
              cualquier descriptor de fichero que este 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 trafico y un
              flujo muy rapido. Ese no va a ser siempre el  caso.  Debe  hacer
              frente  al  caso  en  el  que sus funciones solo logren enviar o
              recibir un unico byte.

       6.     Nunca lea/escriba byte a byte a menos que este realmente  seguro
              de  que  tiene  que  procesar  una pequena 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 podrian facilmente hacerse tan grandes como
              el maximo tamano de paquete posible en su red local.

       7.     Ademas  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 senal,
              entonces es muy poco probable que obtenga EINTR.  Si su programa
              no  activa  E/S no bloqueante, no obtendra EAGAIN.  Sin embargo,
              todavia 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
              tuberia donde el otro extremo ha muerto devuelve cero (al  igual
              que un error de fin de fichero), pero devuelve cero solo una vez
              (un  lectura  o  escritura  posterior  devolvera   -1).   Cuando
              cualquiera  de estas funciones devuelva 0 o -1, no debe pasar el
              descriptor correspondiente a select nunca  mas.  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  oido  que  la  capa  de conectores de Windows no sabe tratar
              adecuadamente los datos OOB. Tampoco sabe tratar llamadas select
              cuando  ningun  descriptor  de  fichero se ha incluido en ningun
              conjunto. No tener ningun descriptor de fichero  activo  es  una
              forma  util  de domir a un proceso con una precision de menos de
              un segundo usando el plazo de tiempo. (Mire mas abajo.)

EMULACI'ON DE USLEEP

       En sistemas que no tienen una funcion 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, solo se garantiza que funcionara en sistemas Unix.

VALOR DEVUELTO

       En caso de exito, select devuelve el numero total de  descriptores  que
       estan presentes todavia en los conjuntos de descriptores de fichero.

       Si  se  cumple  el  plazo  de  espera  para  select,  los  conjuntos de
       descriptores de fichero deberian estar vacios (pero en algunos sistemas
       puede   que   no   sea  asi).   Sin  embargo  el  valor  devuelto  sera
       definitivamente cero.

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

ERRORES

       EBADF  Un  conjunto  contiene  un descriptor de fichero no valido. Este
              error ocurre a menudo cuando anade a un conjunto  un  descriptor
              de fichero sobre el que ya se ha ejecutado la operacion close, o
              cuando ese descriptor de  fichero  ya  ha  experimentado  alguna
              clase  de  error.  Por  tanto  deberia  dejar  de  anadir  a los
              conjuntos cualquier descriptor de fichero que devuelva un  error
              de lectura o escritura.

       EINTR  Una  senal  de interrupcion fue capturada, como SIGINT o SIGCHLD
              etc.   En  este  caso  deberia  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,  tambien  soportan  select. Algunas personas consideran que
       select es una funcion esoterica 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,  senales,  memoria
       compartida y otros oscuros metodos. pselect es una funcion mas reciente
       que es menos comunmente 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  funcion  select  aparecio  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  funcion  pselect esta definida en IEEE Std 1003.1g-2000 (POSIX.1g).
       Se encuentra en glibc2.1 en adelante. Glibc2.0 tiene una funcion con el
       mismo nombre, que sin embargo no acepta un parametro sigmask.

V'EASE TAMBI'EN

       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 pagina de manual fue escrita por Paul Sheer.