2010-04-19 16 views
7

Así que he estado investigando cómo se implementa la parte stdio de libc y me he encontrado con otra pregunta. En cuanto a man setvbuf I ver lo siguiente:¿Quién es el buffer setvbuf de free?

Cuando la primera operación I/O se produce en un archivo, malloc (3) se llama, y ​​se obtiene un tampón .

Esto tiene sentido, su programa no debe tener un malloc para E/S a menos que realmente lo use. Mi reacción instintiva es que libc limpiará su propio desorden aquí. Lo cual solo puedo suponer que lo hace porque valgrind informa que no hay pérdidas de memoria (por supuesto, podrían hacer algo sucio y no asignarlo vía malloc directamente ... pero supondremos que literalmente usa malloc por ahora).

Pero, puede especificar su propio buffer también ...

int main() { 
    char *p = malloc(100); 
    setvbuf(stdio, p, _IOFBF, 100); 
    puts("hello world"); 
} 

Oh no, pérdida de memoria! valgrind lo confirma. Por lo tanto, parece que cada vez que stdio asigna un buffer por sí mismo, se eliminará automáticamente (a más tardar en la salida del programa, pero quizás en el cierre de la transmisión). Pero si especifica el búfer explícitamente, entonces debe limpiarlo usted mismo.

Sin embargo, hay una trampa. La página del manual también dice esto:

Debe asegurarse de que el espacio que puntos a BUF todavía existe cuando flujo está cerrado, que también pasa al acabarse el programa. Por ejemplo, el siguiente es inválido:

Ahora esto se está volviendo interesante para las transmisiones estándar. ¿Cómo podría uno limpiar correctamente un buffer asignado manualmente, ya que están cerrados en la terminación del programa? Podría imaginarse un "limpiar esto cuando cierro la bandera" dentro de la estructura de archivos, pero se ponen peludas porque si leo este derecho haciendo algo como esto:

setvbuf(stdout, 0, _IOFBF, 0); 
printf("hello "); 
setvbuf(stdout, 0, _IOLBF, 0); 
printf("world\n"); 

causaría 2 asignaciones por la biblioteca estándar debido a esta oración:

Si el argumento buf es NULL, solo se ve afectado el modo ; un nuevo buffer será asignado en la siguiente operación de lectura o escritura .

EDIT: una adición a mi pregunta. Dado que está claro que debo free cualquier buffers paso a setvbuf, si de hecho lo uso en stdout ¿hay alguna forma práctica de free? Debe vivir hasta el final del programa. Lo mejor que puedo pensar es en fclose(stdout) y luego liberarlo o usar un buffer estático como algunas personas han mencionado. Lo pregunto porque parece una decisión de diseño incómoda.

Respuesta

3

También desde el man page (al menos, en mi sistema):

Si buf no es NULL, es responsabilidad de la persona que llama libre (3) este tampón después de cerrar el flujo.

Es decir, usted malloc-ed it, lo libera.

Antes de salir, puede cerrar las secuencias usted mismo, lo que le permite liberar el búfer. De forma alternativa, puede vaciar las secuencias y llamar de nuevo al setvbuf con un argumento de almacenamiento intermedio NULL para volver a un búfer administrado por la biblioteca o E/S sin búfer.

+0

Respuesta actualizada al enlace a la página. Es OS X, pero la página del manual proviene de FreeBSD (http://www.freebsd.org/cgi/man.cgi?query=setvbuf). – outis

+0

Difícil ver cómo podrían ser las cosas de otra manera, ya que uno podría proporcionar un búfer estático que no se debe liberar con free(). –

+0

@Neil: de hecho, me preguntaba más si el stdlib tenía una fuga de memoria por diseño, ya que no hay forma de obtener el búfer que asigna de la transmisión. –

1

Puede cerrar stdin, stdout y stderr explícitamente (con fclose()).

Con la mayoría de los sistemas operativos, la memoria dinámica se libera automáticamente al salir del programa. Por lo tanto, no existe un problema práctico con los búferes inéditos (existe un problema cosmético, que es que esos búferes inéditos contaminan la producción de Valgrind). Mi consejo sería usar búferes estáticos si sientes la necesidad de llamar al setvbuf() en la entrada o salida estándar. Los buffers estáticos no necesitan ser asignados o liberados, y son apropiados aquí ya que solo hay tres streams estándar y usted está preocupado por una situación en la que esas transmisiones se mantienen abiertas hasta la finalización del programa.

+0

La salida de depuración contaminada es un problema, porque tiene el hábito de pasar por alto el resultado y puede pasar fácilmente por alto un error real. –

+0

@Thomas: ¿hay alguna garantía de que los archivos se cierren antes de que se libere la memoria? – outis

+0

@outis: en los sistemas operativos donde la memoria se libera automáticamente, se libera "atómicamente", en un punto en el que los hilos de ejecución han dejado de existir. No hay nada que pueda ejecutarse después de eso, en particular, ningún código de libc que podría (erróneamente) acceder al búfer liberado. En resumen, "simplemente funciona". Todavía podría haber algunos archivos abiertos y datos almacenados en el búfer, pero dentro del núcleo del sistema operativo, fuera del alcance del código de aplicación. –

3

Al menos de acuerdo con el estándar C, su último escenario simplemente no está permitido: "La función setvbuf se puede usar solo después de que la secuencia señalada por la secuencia se haya asociado con un archivo abierto y antes de cualquier otra operación (otra que una llamada infructuosa a setvbuf) se realiza en la secuencia ". (C99, §7.19.5.6/2).

En cuanto a cuándo liberar la memoria en los casos más simples, en camino es llamar al atexit() para registrar una devolución de llamada que liberará la memoria después de salir de main(), pero antes de que se devuelva el control al sistema operativo.

+0

interesante. Entonces mi último ejemplo cae en la categoría de comportamiento indefinido. –