Respuestas:
Creo que esto responde a tu pregunta:
De Richard Stevens (rstevens@noao.edu):
La diferencia básica es que fd_set de select () es una máscara de bits y, por lo tanto, tiene un tamaño fijo. Es posible que el kernel no limite este tamaño cuando se compila el kernel, lo que permite que la aplicación defina FD_SETSIZE a lo que quiera (como implican los comentarios en el encabezado del sistema hoy) pero requiere más trabajo. El núcleo de 4.4BSD y la función de biblioteca de Solaris tienen este límite. Pero veo que BSD / OS 2.1 ahora se ha codificado para evitar este límite, por lo que es factible, solo una pequeña cuestión de programación. :-) Alguien debería presentar un informe de error de Solaris sobre esto y ver si alguna vez se soluciona.
Sin embargo, con poll (), el usuario debe asignar una matriz de estructuras pollfd y pasar el número de entradas en esta matriz, por lo que no hay un límite fundamental. Como señala Casper, menos sistemas tienen poll () que select, por lo que este último es más portátil. Además, con las implementaciones originales (SVR3) no puede establecer el descriptor en -1 para decirle al núcleo que ignore una entrada en la estructura pollfd, lo que dificulta la eliminación de entradas de la matriz; SVR4 soluciona esto. Personalmente, siempre uso select () y rara vez sondeo (), porque también transfiero mi código a entornos BSD. Alguien podría escribir una implementación de poll () que use select (), para estos entornos, pero nunca he visto uno. POSIX 1003.1g está estandarizando tanto select () como poll ().
El correo electrónico mencionado anteriormente es al menos tan antiguo como 2001; El poll()
comando ahora (2017) es compatible con todos los sistemas operativos modernos, incluido BSD. De hecho, algunas personas creen que select()
debería ser desaprobado . Dejando a un lado las opiniones, los problemas de portabilidad poll()
ya no son una preocupación en los sistemas modernos. Además, epoll()
desde entonces se ha desarrollado (puede leer la página de manual ) y sigue aumentando en popularidad.
Para el desarrollo moderno, probablemente no desee utilizarlo select()
, aunque no hay nada explícitamente malo en ello. poll()
, y es una evolución más moderna epoll()
, proporciona las mismas características (y más) que select()
sin sufrir las limitaciones en él.
select
o poll
:(
La select()
llamada hace que cree tres máscaras de bits para marcar qué conectores y descriptores de archivos desea observar para leer, escribir y errores, y luego el sistema operativo marca cuáles de hecho han tenido algún tipo de actividad; poll()
tiene que crear una lista de ID de descriptores, y el sistema operativo marca cada uno de ellos con el tipo de evento que ocurrió.
El select()
método es bastante torpe e ineficiente.
Por lo general, hay más de mil descriptores de archivo potenciales disponibles para un proceso. Si un proceso de larga duración solo tiene unos pocos descriptores abiertos, pero al menos a uno de ellos se le ha asignado un número alto, entonces la máscara de bits pasada select()
debe ser lo suficientemente grande como para acomodar ese descriptor más alto, por lo que se obtendrán rangos enteros de cientos de bits asegúrese de que el sistema operativo tenga que recorrer cada select()
llamada solo para descubrir que no están configurados.
Una vez que select()
regresa, la persona que llama tiene que recorrer las tres máscaras de bits para determinar qué eventos tuvieron lugar. En muchas aplicaciones típicas, solo uno o dos descriptores de archivo obtendrán tráfico nuevo en un momento dado, sin embargo, las tres máscaras de bits deben leerse hasta el final para descubrir qué descriptores son.
Debido a que el sistema operativo le indica sobre la actividad reescribiendo las máscaras de bits, se arruinan y ya no están marcadas con la lista de descriptores de archivos que desea escuchar. Debe reconstruir toda la máscara de bits a partir de alguna otra lista que tenga en la memoria, o debe mantener una copia duplicada de cada máscara de bits y memcpy()
el bloque de datos encima de las máscaras de bits arruinadas después de cada select()
llamada.
Por lo tanto, el poll()
enfoque funciona mucho mejor porque puede seguir reutilizando la misma estructura de datos.
De hecho, poll()
ha inspirado otro mecanismo en los núcleos modernos de Linux: epoll()
que mejora aún más el mecanismo para permitir otro salto en la escalabilidad, ya que los servidores de hoy en día a menudo quieren manejar decenas de miles de conexiones a la vez. Esta es una buena introducción al esfuerzo:
http://scotdoyle.com/python-epoll-howto.html
Si bien este enlace tiene algunos gráficos agradables que muestran los beneficios de epoll()
(notará que select()
en este punto se considera tan ineficiente y anticuado que ni siquiera aparece una línea en estos gráficos):
http://lse.sourceforge.net/epoll/index.html
Actualización: Aquí hay otra pregunta de desbordamiento de pila, cuya respuesta brinda aún más detalles sobre las diferencias:
Advertencias de los reactores select / poll vs. reactores epoll en Twisted
Ambos son lentos y en su mayoría iguales , ¡pero diferentes en tamaño y algún tipo de características!
Cuando escribes un iterador, ¡debes copiar el conjunto de select
cada vez! Si bien se poll
ha solucionado este tipo de problema para tener un código hermoso. Otra diferencia es que poll
puede manejar más de 1024 descriptores de archivo (FD) de forma predeterminada. poll
puede manejar diferentes eventos para hacer que el programa sea más legible en lugar de tener muchas variables para manejar este tipo de trabajo. Las operaciones en poll
y select
son lineales y lentas debido a que tiene muchas comprobaciones.