Consulta sobre llamada de sistema selecto

select () se define como:

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

nfds representa el descriptor de archivo más alto en todos los conjuntos dados más uno. Me gustaría saber por qué se requieren estos datos para seleccionar () cuando la información fd_set está disponible.

Si las FD del conjunto son, por ejemplo, 4, 8, 9, el valor de nfds sería 10. ¿Seleccionaría () moniter fds 9,8,7,6,5,4?

El problema es que fd_set no es realmente un “conjunto” en la forma en que estás pensando. El detalle detrás de las escenas es que la implementación de un fd_set es solo un número entero que se usa como un campo de bits. En otras palabras, ejecutar

 fd_set foo; FD_CLEAR(&foo); FD_SET(&foo, 3); 

Establece foo en el valor decimal 8: establece el cuarto bit menos singular en 1 (recuerde que 0 es un descriptor válido).

 FD_SET(&foo, 3); 

es equivalente a

 foo |= (1 << 3); 

Entonces, para que la selección funcione correctamente, necesita saber qué bits del fd_set son los bits que le interesan. De lo contrario, no habría forma de decirle a un bit cero que está "en" el conjunto, pero establecido en falso desde un bit cero que "no está en" el conjunto.

En su ejemplo, un fd_set con el conjunto 4, 8 y 9 y n = 10 se interpreta como "Un conjunto con 10 entradas (fds 0-9). Las entradas 4, 8 y 9 son verdaderas (las supervise). Las entradas 1 , 2,3,5,6,7 son falsos (no los supervise). Cualquier valor de fd mayor que 9 simplemente no se encuentra en el período establecido ".

Select supervisa las FD que ha habilitado utilizando la macro FD_SET. Si no habilita ningún FD para monitoreo, seleccione () no monitorea ninguno.

“nfds” es definitivamente redundante, pero es parte de la interfaz select (), así que necesitas usarlo 🙂

De todos modos, si tiene {4, 8, 9} en el conjunto, establece nfds en 10 (como mencionó), y seleccionando () solo monitoreará las tres FD 4, 8 y 9.

Probablemente sea una optimización para que fd_set no tenga que recorrer todo el fd_set para descubrir qué descriptores se utilizan realmente. Sin ese parámetro, la select siempre tendría que mirar todo el conjunto para encontrar qué descriptores se usan realmente en la llamada, con el parámetro, parte de ese trabajo se puede omitir.