2009-02-12 9 views
24

¿Cuál es el comportamiento de la función select(2) cuando un descriptor de archivo que está mirando para leer está cerrado por otro hilo?¿Qué hace que seleccione (2) si cierra (2) un descriptor de archivo en un hilo separado?

A partir de algunas pruebas superficiales, regresa de inmediato. Sospecho que el resultado es que (a) todavía continúa esperando datos, pero si realmente intentas leer de él obtendrás EBADF (posiblemente - hay una carrera potencial) o (b) que pretende ser como si el descriptor de archivo nunca se pasó. Si el último caso es cierto, pasar un solo fd sin tiempo de espera provocaría un interbloqueo si se cerrase.

+0

Posible duplicado de [ruptura de selección de socket] (http://stackoverflow.com/questions/2486727/breaking-out-from-socket-select) – iammilind

+0

Creo que es diferente, aunque ligeramente relacionado. La otra pregunta es preguntar explícitamente cómo salir de un 'select()' de otro hilo (y 'pipe()' es una buena respuesta), mientras que el mío era más sobre el comportamiento de 'close()' en un 'select () 'conector ed. En las respuestas a continuación, verá que la respuesta es "depende". –

+0

Una de las búsquedas de errores más misteriosas que surgió se debió a este problema: el hilo A estaba seleccionando en el socket #x, que el hilo B cerró. Poco después, el subproceso C creó un nuevo socket, que también resultó ser el socket #x (porque la pila de red eligió reutilizar el número x para el nuevo socket). En este punto, el hilo A (que todavía estaba tratando de usar el socket #x), por supuesto, comenzó a seleccionar/leer/escribir datos en el socket de la rosca C, a pesar de que no tenían absolutamente ninguna conexión lógica entre sí. Este fue un dolor total para rastrear. –

Respuesta

21

de alguna investigación adicional, parece que tanto dwc y Bothie tienen razón.

bothie's answer a la cuestión se reduce a: es un comportamiento indefinido.Eso no significa que sea impredecible necesariamente, sino que diferentes sistemas operativos lo hacen de manera diferente. Parecería que los sistemas como Solaris y HP-UX retorno de select(2) en este caso, pero Linux no basan en this post to the linux-kernel mailing list de 2001.

El argumento en la lista de correo linux-kernel es esencialmente que no está definido (y rotas) comportamiento en el que confiar. En el caso de Linux, llamar al close(2) en el descriptor de archivo disminuye efectivamente un recuento de referencia en él. Como hay una llamada a select(2) también con una referencia, el fd permanecerá abierto y esperando la entrada hasta que el select(2) regrese. Esto es básicamente dwc's answer. Obtendrá un evento en el descriptor del archivo y luego se cerrará. Intentar leer de él dará como resultado un EBADF, suponiendo que el fd no se haya reciclado. (Una preocupación que MarkR hizo en his answer, aunque creo que probablemente sea evitable en la mayoría de los casos con la sincronización adecuada.)

Gracias a todos por la ayuda.

+0

Es necesariamente impredecible. No hay una forma predecible de 'cerrar' un descriptor de archivo en un subproceso mientras otro subproceso lo está viendo en 'seleccionar' porque no hay forma de que el subproceso que llama' cerrar' pueda saber si el otro subproceso ya está bloqueado o no en 'seleccionar' o a punto de bloquear en' seleccionar'. –

6

Supongo que se comportaría como si se hubiera alcanzado el final del archivo, es decir, volvería con el descriptor de archivo mostrado como listo, pero cualquier intento de leerlo posteriormente devolvería el "descriptor de archivo incorrecto". ".

Dicho esto, haciendo que es muy mala práctica de todos modos, ya que siempre tendría potenciales condiciones de carrera como otro descriptor de archivo con el mismo número podría ser abierto por otro hilo inmediatamente después del otro segundo cerró, entonces el hilo de selección terminaría esperando en el incorrecto.

Tan pronto como cierre un archivo, su número estará disponible para su reutilización, y puede volver a ser utilizado por la próxima llamada a abrir(), socket(), etc., incluso por otro hilo. Por lo tanto, realmente, realmente debes evitar este tipo de cosas.

+0

Pensé que podría volver también como listo, pero eso no está del todo bien: el descriptor no está realmente listo: está cerrado. Y como mencionas, para cuando la uses podría ser reasignada a otra cosa. –

+0

Sin embargo, puedes evitar la carrera usando un mutex para la estructura de datos que contiene el fd. Pero eso solo funcionaría si la llamada select() tuviera un tiempo de espera definido. –

2

Es un poco confuso lo que preguntas ...

Seleccionar() debe devolver a un cambio "interesante". Si el cierre() simplemente disminuía el recuento de referencias y el archivo aún estaba abierto para escribir en algún lugar, entonces no hay ninguna razón para que select() se active.

Si el otro hilo se cerró() en el único descriptor abierto, entonces se vuelve más interesante, pero necesitaría ver una versión simple del código para ver si algo está realmente mal.

5

La llamada al sistema de selección es una forma de esperar a que los descriptores de archivos cambien de estado mientras que los programas no tienen nada más que hacer. El uso principal es para aplicaciones de servidor, que abren una gran cantidad de descriptores de archivos y luego esperan que ocurra algo sobre ellos (acepte nuevas conexiones, lea solicitudes o envíe las respuestas). Esos descriptores de archivos se abrirán en modo no bloqueante, de modo que el proceso del servidor no se bloqueará en un syscall en ningún momento.

Esto significa, además, que no hay necesidad de hilos separados, porque todo el trabajo que se podría hacer en el hilo también se puede hacer antes de la llamada de selección. Y si el trabajo lleva mucho tiempo, que puede ser interrumpido, seleccione ser llamado con timeout = {0,0}, se manejan los descriptores de archivo y luego se reanuda el trabajo.

Ahora, cierra un descriptor de archivo en otro hilo. ¿Por qué tiene ese hilo adicional y por qué cerrará el descriptor del archivo?

estándar POSIX no proporciona ninguna pista, lo que sucede en este caso, por lo que lo que está haciendo es un comportamiento indefinido. Espere que el resultado sea muy diferente entre diferentes sistemas operativos e incluso entre versiones del mismo sistema operativo.

Saludos, Bodo

+1

Creo que tendrá un comportamiento indefinido de todos modos, porque es imposible eliminar la condición de carrera del descriptor de archivo que se cierra * justo antes * de la selección y otro que se abre con el mismo número. – MarkR

Cuestiones relacionadas