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.

Linux 2.4                                        21 octubre 2001                                   SELECT_TUT(2)