¿Qué sucede cuando cierro () un descriptor de archivo?

16

Estoy tratando de obtener la imagen completa con descriptores de archivo. Digamos que tengo process1 que inicialmente tiene estos descriptores de archivo:

 _process1_
|          |
| 0 stdin  |
| 1 stdout |
| 2 stderr |
|__________|

Luego cierro el descriptor de archivo 1:

close(1);

El descriptor de archivo 1 se traduce (puntos) a la estructura de archivo estándar en la tabla de archivos abiertos del núcleo .

Con el código anterior, el descriptor de archivo 1 se elimina de la tabla del proceso que se convierte en:

 _process1_
|          |
| 0 stdin  |
| 2 stderr |
|__________|

¿Pero qué pasa en el núcleo? ¿La stdoutestructura FILE se desasigna? ¿Cómo es eso posible si stdout es un archivo especial (el monitor) y probablemente sea utilizado por otros procesos? ¿Qué pasa con las estructuras de ARCHIVO que son solo archivos normales (.txt, por ejemplo)? ¿Qué sucede si dicho archivo está siendo utilizado por otro proceso?

Pithikos
fuente

Respuestas:

13

El descriptor de archivo 1 se traduce en la estructura de archivo estándar en la tabla de archivos abiertos del núcleo.

Este es un malentendido. La tabla de archivos del núcleo no tiene nada que ver con las estructuras de archivos del espacio de usuario.

En cualquier caso, el núcleo tiene dos niveles de indirección. Existe la estructura interna que representa el archivo en sí, que se cuenta como referencia. Hay una "descripción de archivo abierto" que se cuenta como referencia. Y luego está el identificador de archivo, que no se cuenta como referencia. La estructura del archivo señala el camino hacia el propio inodo. La descripción del archivo abierto contiene cosas como el modo abierto y el puntero del archivo.

Cuando llamas a close, siempre cierras el identificador de archivo. Cuando se cierra un identificador de archivo, el recuento de referencias en su descripción de archivo abierto disminuye. Si va a cero, la descripción del archivo abierto también se libera y el recuento de referencias en el archivo en sí mismo se reduce. Solo si eso llega a cero se libera la estructura de archivos del núcleo.

No hay posibilidad de que un proceso libere un recurso que otro proceso está usando porque los recursos compartidos se cuentan por referencia.

David Schwartz
fuente
Tengo una ligera dificultad con la comprensión de la terminología en su respuesta. Supongo que el puntero de archivo significa "desplazamiento de archivo". ¿Es eso lo que querías decir? ¿También a qué se refería con identificador de archivo ?
Geek
Eso es correcto, por "desplazamiento de archivo", me refiero al desplazamiento en el que ocurriría una lectura o escritura posterior. Un "identificador de archivo" es un enlace entre un proceso y una descripción de archivo abierto: es lo que obtienes cuando tienes openéxito.
David Schwartz
6

En este caso no sucederá mucho. stdin, stdout y stderr tienden a ser clones del mismo descriptor de archivo. El contador de referencia para el descriptor de archivo se reducirá en uno. El mismo descriptor de archivo generalmente se encuentra en el shell desde el que se ejecutó el programa, por lo que debe mantenerse el descriptor de archivo.

El núcleo mantiene recuentos de referencia para todos los archivos (inodos) que están abiertos. Mientras el recuento de referencia sea mayor que cero, el archivo se mantendrá. Esperaría que se mantenga un contador separado para los identificadores de archivos abiertos. Una vez que esto llega a cero, el núcleo puede liberar la memoria utilizada por el identificador de archivo.

Cuando se hayan eliminado todas las referencias al archivo (entradas de directorio y identificadores de archivo), el código del sistema de archivos marcará el inodo para su reutilización. Cualquier bloque que tenga el archivo estará disponible para su asignación. Muchos sistemas de archivos borrarán los punteros de bloque en el inodo cuando se libere. Esto dificulta la recuperación de un archivo eliminado. Las actualizaciones en el disco pueden almacenarse y completarse más adelante.

BillThor
fuente
1
Dos preguntas: (1) ¿se cuentan realmente los descriptores de archivo? Cuando controlas-d a cat > some.file, cat obtiene un EOF en stdin, pero el shell no. (2) ¿Por qué contar las referencias? ¿Por qué no alguna forma de recolección de basura? ¿GC no es mucho mejor en espacio de usuario?
Bruce Ediger el
Ampliando la respuesta de BillThor: En casos normales, stdin, stdout y stderr son solo identificadores de archivos abiertos en un dispositivo TTY. Entonces, si cierra el identificador de archivo, ese dispositivo TTY todavía está allí, e incluso puede volver a abrirse más adelante.
Patrick
1
@BruceEdiger: (1) cuando el shell ejecuta cat > some.filelo que realmente está haciendo es bifurcación, abre 'some.file' y lo asigna al descriptor de archivo 1, luego lo hace exec("cat"). Cuando un proceso es exec () 'd, hereda los descriptores de archivo abiertos.
Patrick
@BruceEdiger (2) El conteo de referencias es una forma perfectamente fina de recolección de basura cuando se usa en estructuras de datos que no contienen punteros a (o cadenas de punteros que terminan en) otras estructuras de datos del mismo tipo. Además, esto está sucediendo en el espacio del kernel (no es que importe mucho).
Gilles 'SO- deja de ser malvado'