2010-04-19 8 views
7

J'ai donc creusé la façon dont la partie stdio de libc est implémentée et j'ai rencontré une autre question. En regardant man setvbuf je vois le suivant:qui setvbuf tampon libre?

Lorsque la première opération I/O se produit sur un fichier, malloc (3) est appelé, et un tampon est obtenu.

Cela est logique, votre programme ne devrait pas avoir un malloc dedans pour E/S à moins que vous utilisez réellement. Ma réaction viscérale à ceci est que la libc nettoiera son propre désordre ici. Ce que je ne peux que supposer, c'est parce que valgrind ne signale aucune fuite de mémoire (ils pourraient bien sûr faire quelque chose de sale et ne pas l'allouer via malloc directement ... mais nous supposerons qu'il utilise littéralement malloc pour l'instant).

Mais, vous pouvez spécifier votre propre tampon trop ...

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

Oh non, fuite de mémoire! valgrind le confirme. Il semble donc que chaque fois que stdio alloue une mémoire tampon, elle sera automatiquement supprimée (au plus tard lors de la sortie du programme, mais peut-être aussi lors de la fermeture du flux). Mais si vous spécifiez explicitement le tampon, vous devez le nettoyer vous-même.

Il y a cependant un problème. La page de manuel dit aussi ceci:

Vous devez vous assurer que l'espace qui points de buf existe encore au moment flux est fermé, ce qui arrive aussi à la fin du programme. Par exemple, suivant est incorrect:

Maintenant cela devient intéressant pour les flux standards. Comment pourrait-on nettoyer correctement un tampon alloué manuellement pour eux, puisqu'ils sont fermés dans la terminaison du programme? Je ne pouvais imaginer un « nettoyer ce quand je ferme le drapeau » dans le struct fichier, mais il se poilue parce que si je lis ce droit de faire quelque chose comme ceci:

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

causerait 2 allocations par la bibliothèque standard en raison de cette phrase:

Si l'argument buf est NULL, seul le mode est affecté; un nouveau tampon sera alloué lors de la prochaine opération de lecture ou d'écriture .

EDIT: un addenda à ma question. Comme il est clair que je dois free tampons que je passe à setvbuf, si je l'utilise en fait sur stdout est-il un moyen pratique de free il? Il doit vivre à la fin du programme. Le mieux que je puisse penser est fclose(stdout) puis le libérer ou utiliser un tampon statique comme certaines personnes l'ont mentionné. Je demande parce que cela semble être une décision de conception maladroite.

Répondre

3

également de la man page (au moins, sur mon système):

Si buf est NULL, il est ce tampon après la fermeture du flux de la responsabilité de libérer de l'appelant (3).

C'est, vous malloc-ed, vous le libérez.

Avant la sortie, vous pouvez fermer les flux vous-même, ce qui vous permet de libérer le tampon. Vous pouvez également vider les flux et appeler à nouveau setvbuf avec un argument de tampon NULL pour revenir à un tampon géré de bibliothèque ou à des E/S non tamponnées.

+0

Mise à jour de la réponse vers un lien vers la page. C'est OS X, mais la page de manuel vient de FreeBSD (http://www.freebsd.org/cgi/man.cgi?query=setvbuf). – outis

+0

Difficile de voir comment les choses pourraient être autrement, car on pourrait fournir un tampon statique qui ne doit pas être libéré avec free(). –

+0

@Neil: en effet, je me demandais plus si le stdlib avait une fuite de mémoire à cause de la conception car il n'y a aucun moyen d'obtenir le tampon qu'il a affecté hors du flux. –

1

Vous pouvez fermer stdin, stdout et stderr explicitement (avec fclose()).

Avec la plupart des systèmes d'exploitation, la mémoire de tas est automatiquement libérée lors de la sortie du programme. Il n'y a donc pas de problème pratique avec les tampons inédits (il y a un problème esthétique, à savoir que ces tampons inédits polluent la sortie de Valgrind). Mon conseil serait d'utiliser des tampons statiques si vous ressentez l'envie d'appeler setvbuf() sur l'entrée ou la sortie standard. Les tampons statiques n'ont pas besoin d'être alloués ou libérés, et sont appropriés ici car il n'y a que trois flux standards et vous vous inquiétez d'une situation où ces flux restent ouverts jusqu'à la fin du programme.

+0

Une sortie de débogage polluée est un problème, car vous avez l'habitude de masquer la sortie et vous pouvez facilement passer à côté d'une erreur réelle. –

+0

@Thomas: Y at-il une garantie que les fichiers seront fermés avant que la mémoire soit libérée? – outis

+0

@outis: sur les systèmes d'exploitation où la mémoire est automatiquement libérée, elle est libérée "atomiquement", à un point où les threads d'exécution ont cessé d'exister. Il n'y a rien qui puisse fonctionner après cela, en particulier aucun code libc qui pourrait (à tort) accéder au tampon libéré. En bref, cela "fonctionne". Il pourrait toujours y avoir des fichiers ouverts et des données tamponnées, mais dans le noyau du système d'exploitation, hors de la portée du code applicatif. –

3

Au moins selon la norme C, votre dernier scénario n'est tout simplement pas autorisé: "La fonction setvbuf ne peut être utilisée qu'après que le flux pointé par flux a été associé à un fichier ouvert et avant toute autre opération (autre qu'un appel infructueux à setvbuf) est effectué sur le flux. " (C99, §7.19.5.6/2).

Quant à savoir quand pour libérer la mémoire dans les cas les plus simples, sur le chemin est d'appeler atexit() pour enregistrer un rappel qui libérera la mémoire après avoir quitté main(), mais avant que le contrôle est retourné au système d'exploitation.

+0

intéressant. Donc mon dernier exemple tombe dans la catégorie du comportement indéfini. –

Questions connexes