Cómo utilizar select() y poll() para multiplexar sockets de red

Los métodos select() y poll() pueden ser una herramienta poderosa cuando se multiplexan sockets de red. Específicamente, estos métodos indicarán cuándo es seguro ejecutar un procedimiento en un descriptor de archivo abierto sin ningún retraso. Por ejemplo, un programador puede usar estas llamadas para saber cuándo hay datos para ser leídos en un socket. Al delegar la responsabilidad a select() y poll(), no tienes que verificar constantemente si hay datos para leer. En cambio, select() y poll() pueden ejecutarse en segundo plano por el sistema operativo y activarse cuando se cumple el evento o ha transcurrido un tiempo determinado. Este proceso puede aumentar significativamente la eficiencia de ejecución de un programa. (Si te preocupa más el rendimiento que la portabilidad, discutiremos algunas alternativas a select() y poll() al final del artículo).

Índice de Contenido
  1. Semejanzas entre select() y poll()
  2. Descripción de select()
  3. Descripción de poll()
  4. Alternativas a select() y poll()
  5. /dev/poll de Solaris
  6. kqueue de FreeBSD

Semejanzas entre select() y poll()

Como verás, select() y poll() son muy similares en funcionalidad. A menudo, las implementaciones de select() y poll() se mapean una en la otra. Por ejemplo, en la API portable de Apache, un componente central de Apache 2.0, se proporciona una interfaz portátil que imita la semántica de poll(). En plataformas que no tienen una implementación nativa de poll(), la semántica de poll() se mapea a select(). En FreeBSD, la implementación libc_r de select() es simplemente un envoltorio delgado alrededor del llamado al sistema poll().

Descripción de select()

La Especificación Única de UNIX, versión 2 (SUSv2) define select() de la siguiente manera:

int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *errorfds, struct timeval *timeout);

Toma los siguientes parámetros:

  • int nfds - El descriptor de archivo más alto en todos los conjuntos dados más uno.
  • fd_set *readfds - Descriptores de archivo que desencadenarán un retorno cuando haya datos listos para ser leídos.
  • fd_set *writefds - Descriptores de archivo que desencadenarán un retorno cuando haya datos listos para ser escritos.
  • fd_set *errorfds - Descriptores de archivo que desencadenarán un retorno cuando ocurra una excepción.
  • struct timeval *timeout - El período máximo que select() debe esperar a un evento.

El valor de retorno indica el número de descriptores de archivo (fds) cuyo evento solicitado se ha cumplido.

Cómo utilizar Isearch para buscar archivos de texto en Linux

No puedes modificar directamente la estructura fd_set cambiando su valor. La única forma portátil de establecer o recuperar el valor es utilizando las macros FD_* proporcionadas:

  • FD_ZERO(fd_set *) - Inicializa un fd_set vacío.
  • FD_CLR(int fd, fd_set *) - Elimina el fd asociado del fd_set.
  • FD_SET(int fd, fd_set *) - Agrega el fd asociado al fd_set.
  • FD_ISSET(int fd, fd_set *) - Devuelve un valor distinto de cero si el fd está en fd_set.

Después de llamar a select(), se puede llamar a FD_ISSET() para cada fd en un conjunto dado para identificar si se ha cumplido su condición.

Con el valor timeout, puedes especificar cuánto tiempo esperará select() un evento. Si timeout es NULL, select() esperará indefinidamente a un evento. Si timeout se establece en 0, select() regresará inmediatamente en lugar de esperar a que ocurra cualquier evento. De lo contrario, timeout define cuánto tiempo esperará select(). SUSv2 indica que todas las implementaciones compatibles admitirán un tiempo de espera de al menos 31 días. Consulta el listado A para obtener un ejemplo de select().

Descripción de poll()

El método poll() intenta consolidar los argumentos de select() y proporciona notificación de una gama más amplia de eventos. La especificación de SUSv2 define poll() de la siguiente manera:

int poll(struct pollfd fds[ ], nfds_t nfds, int timeout);

Toma los siguientes parámetros:

Guía para entender y gestionar los permisos de archivos y directorios en Linux
  • struct pollfd fds[ ] - Un array de estructuras pollfd.
  • nfds_t nfds - El número de descriptores de archivo establecidos en fds[ ].
  • int timeout - Cuánto tiempo poll() debe esperar a que ocurra un evento (en milisegundos).

El valor de retorno indica cuántos fds tuvieron un evento.

Por lo general, una estructura pollfd incluye los siguientes campos:

  • int fd - Indica qué fd se debe monitorear para un evento.
  • short events - Un campo de bits que representa qué eventos se deben monitorear.
  • short revents - Un campo de bits que representa qué eventos fueron detectados en una llamada a poll().

La especificación de SUSv2 detalla el valor y significado precisos del bitfield de eventos. En comparación con select(), poll() permite un mayor grado de flexibilidad al determinar qué tipo de eventos se pueden procesar. Además de las notificaciones de lectura, escritura y errores, poll() también admite el reconocimiento explícito de datos fuera de banda y de alta prioridad.

A diferencia de select(), el timeout de poll() es un entero simple que representa cuánto tiempo poll() esperará un evento. Un valor especial, generalmente -1 o la constante INFTIM, que se utiliza en muchos sistemas antiguos, especifica que poll() esperará indefinidamente a un evento. Al igual que select(), un valor de timeout de 0 indica que poll() debe regresar inmediatamente.

En el Listado B, se muestra el ejemplo de select() del Listado A como poll().

Alternativas a select() y poll()

A pesar de las ventajas de tener notificaciones de eventos multiplexados a través de select() o poll(), existen otras implementaciones que ofrecen un mejor rendimiento. Sin embargo, estas implementaciones no están estandarizadas en todas las plataformas. Debes sopesar los posibles beneficios de rendimiento al utilizar una de estas implementaciones especializadas frente a la pérdida de portabilidad. Vamos a examinar dos de estas alternativas: /dev/poll de Solaris y kqueue de FreeBSD.

Cómo configurar un servidor Linux como puerta de enlace para una red local

Como verás, ambas implementaciones obtienen sus principales beneficios de rendimiento aprovechando el hecho de que, en el mundo real, los desarrolladores llaman constantemente a select() o poll() con los mismos fds. Para eliminar la sobrecarga al pasar los mismos argumentos cada vez, los fds que se examinan se pueden almacenar en caché. Este enfoque también funciona bien cuando se monitorea un gran número de fds, ya que algunas implementaciones de select() y poll() tienen problemas de escalabilidad.

/dev/poll de Solaris

En Solaris 7, Sun presentó el dispositivo /dev/poll. Para utilizar /dev/poll, primero debes abrir /dev/poll como lo harías con un archivo normal. Luego, construyes estructuras pollfd de manera similar a la llamada normal a poll(). Estas estructuras pollfd se escriben en el descriptor de archivo abierto de /dev/poll. Durante la vida útil de este controlador abierto, /dev/poll ahora devolverá eventos de acuerdo con esa estructura pollfd. (Ten en cuenta que un valor especial POLLREMOVE en el campo de eventos de la estructura pollfd eliminará ese fd de la lista de /dev/poll). Un programa recupera la información de /dev/poll llamando a un ioctl especial (DP_POLL) y una estructura dvpoll. Mediante el uso de la estructura dvpoll, se pueden determinar los eventos que han ocurrido.

kqueue de FreeBSD

Introducida en FreeBSD 4.1, la API kqueue de FreeBSD está diseñada para abordar una variedad más amplia de notificaciones que cualquiera de las otras alternativas presentadas aquí. La API kqueue proporciona varios filtros genéricos que permiten imitar la semántica de poll() (EVFILT_READ y EVFILT_WRITE). Sin embargo, también permite la notificación de cambios en el sistema de archivos (EVFILT_VNODE), cambios en el estado del proceso (EVFILT_PROC) y entrega de señales (EVFILT_SIGNAL). Para obtener más información sobre kqueue, descarga el artículo (en formato PDF) de Jonathan Lemon de BSDCon 2000, "Kqueue: A generic and scalable event notification facility".

Para ampliar la información sobre select() y poll() presentada en este artículo, te recomiendo encarecidamente el libro "Programación Avanzada en el Entorno UNIX" de W. Richard Stevens.

Windows al alcance de Linux: Accede a recursos con smbmount y smbclient

En Newsmatic nos especializamos en tecnología de vanguardia, contamos con los artículos mas novedosos sobre Código abierto, allí encontraras muchos artículos similares a Cómo utilizar select() y poll() para multiplexar sockets de red , tenemos lo ultimo en tecnología 2023.

Artículos Relacionados

Subir

Utilizamos cookies para mejorar su experiencia de navegación, mostrarle anuncios o contenidos personalizados y analizar nuestro tráfico. Al hacer clic en “Aceptar todo” usted da su consentimiento a nuestro uso de las cookies.