solicita - ¿Es una buena práctica cerrar los descriptores de archivos al salir?
llamada al sistema exec (6)
Por lo que sé, estos archivos se cierran automáticamente cuando el proceso muere.
No confíes en eso. Conceptualmente, cuando el proceso muere, es su responsabilidad liberar la memoria asignada, cerrar los descriptores de archivos no estándar, etc. Por supuesto, todos los sistemas operativos sanos (e incluso Windows) se limpiarán después de su proceso, pero eso no es algo que se pueda esperar.
Si por alguna razón, descubro una situación fatal en mi programa, y me gustaría salir con un código de error. A veces, el contexto del error fatal está fuera del alcance de otros descriptores de archivo. ¿Es una buena práctica cerrar estos descriptores de archivo? Por lo que sé, estos archivos se cierran automáticamente cuando el proceso muere.
C garantiza que todos los archivos abiertos se cerrarán si su programa finaliza normalmente (es decir, a través de la exit
o un retorno de main
). Sin embargo, si su programa termina de forma anormal, por ejemplo, el sistema operativo lo cierra debido a que utiliza un puntero NULO, depende del sistema operativo cerrar los archivos. Por lo tanto, es una buena idea asegurarse de que los archivos estén cerrados una vez que ya no sean necesarios en caso de una terminación inesperada.
La otra razón es el límite de recursos. La mayoría de los sistemas operativos tienen límites en la cantidad de archivos abiertos (así como en muchas otras cosas), por lo que es una buena práctica devolver esos recursos tan pronto como ya no sean necesarios. Si todos los programas mantuvieran abiertos todos sus archivos por tiempo indefinido, los sistemas podrían tener problemas muy rápidamente.
Cada sistema operativo sano (ciertamente cualquier forma de Linux o Windows) cerrará los archivos cuando el programa termine. Si tiene un programa muy simple, entonces probablemente no necesite cerrar los archivos al finalizar. Sin embargo, cerrar los archivos explícitamente sigue siendo una buena práctica, por las siguientes razones:
si lo deja en el sistema operativo, no tiene control sobre el orden en que se cierran los archivos, lo que puede ocasionar problemas de coherencia (como en una base de datos de varios archivos).
Si hay errores asociados con el cierre del archivo (como errores de E / S, errores de falta de espacio, etc.) no tiene forma de informarlos.
puede haber interacciones con el bloqueo de archivos que deben manejarse.
una rutina para cerrar todos los archivos puede manejar cualquier otra limpieza que el programa necesite al mismo tiempo (búferes de descarga, por ejemplo)
La guía clásica para la programación POSIX "Programación avanzada en entorno UNIX" dice:
Cuando un proceso termina, todos los archivos abiertos se cierran automáticamente por el núcleo. Muchos programas aprovechan este hecho y no cierran explícitamente los archivos abiertos.
No mencionó el SO en su pregunta, pero tal comportamiento debería esperarse de cualquier SO. Siempre que el flujo de control de su programa se cruce con exit()
o el return
de main()
, es responsabilidad del sistema limpiar después del proceso.
Siempre hay un peligro de errores en la implementación del sistema operativo. Pero, por otro lado, el sistema tiene que desasignar más que varios descriptores de archivos abiertos en la terminación del proceso: memoria ocupada por la imagen del archivo ejecutable, la pila, los objetos del núcleo asociados con el proceso. No puede controlar este comportamiento desde el espacio del usuario, solo confíe en su funcionamiento según lo previsto. Entonces, ¿por qué un programador no puede confiar en el cierre automático de los fd
s?
Por lo tanto, el único problema con dejar abierto fd
s puede ser la pregunta sobre el estilo de programación. Y, como en el caso del uso de objetos stdio
(es decir, cosas creadas alrededor del archivo i / o provisto por el sistema), puede obtener (de alguna manera) alertas desorientadoras mientras realiza la comprobación. En cuanto al peligro de perder recursos del sistema, no debería haber nada de qué preocuparse, a menos que la implementación de su sistema operativo sea realmente defectuosa.
Los archivos se cierran automáticamente, pero es una buena práctica.
Ver valgrind en este ejemplo.
david@debian:~$ cat demo.c
#include <stdio.h>
int main(void)
{
FILE *f;
f = fopen("demo.c", "r");
return 0;
}
david@debian:~$ valgrind ./demo
==3959== Memcheck, a memory error detector
==3959== Copyright (C) 2002-2010, and GNU GPL''d, by Julian Seward et al.
==3959== Using Valgrind-3.6.0.SVN-Debian and LibVEX; rerun with -h for copyright info
==3959== Command: ./demo
==3959==
==3959==
==3959== HEAP SUMMARY:
==3959== in use at exit: 568 bytes in 1 blocks
==3959== total heap usage: 1 allocs, 0 frees, 568 bytes allocated
==3959==
==3959== LEAK SUMMARY:
==3959== definitely lost: 0 bytes in 0 blocks
==3959== indirectly lost: 0 bytes in 0 blocks
==3959== possibly lost: 0 bytes in 0 blocks
==3959== still reachable: 568 bytes in 1 blocks
==3959== suppressed: 0 bytes in 0 blocks
==3959== Rerun with --leak-check=full to see details of leaked memory
==3959==
==3959== For counts of detected and suppressed errors, rerun with: -v
==3959== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 4 from 4)
Como puedes ver, se levanta una pérdida de memoria.
En algunas circunstancias puedes hacer uso de atexit()
:
#include <stdio.h>
#include <stdlib.h>
static FILE *f;
static void free_all(void)
{
fclose(f);
}
static int check(void)
{
return 0;
}
int main(void)
{
atexit(free_all);
f = fopen("demo.c", "r");
if (!check()) exit(EXIT_FAILURE);
/* more code */
return 0;
}
Sí. Supongamos que su programa principal es ahora una clase en un programa separado. Ahora que acaba de describir una fuga de recursos. Básicamente, está violando la encapsulación al confiar en el estado global del programa, es decir, el estado del proceso, no su módulo, ni una clase, ni un ADT, ni un subproceso, sino todo el proceso: estar en un estado de apagado.