ist es eine gute Praxis, Dateideskriptoren beim Beenden zu schließen

Lesezeit: 5 Minuten

Wenn ich aus irgendeinem Grund eine schwerwiegende Situation in meinem Programm entdecke und es mit einem Fehlercode beenden möchte. Manchmal liegt der Kontext des schwerwiegenden Fehlers außerhalb des Geltungsbereichs anderer Dateideskriptoren. ist es eine gute Praxis, diese Dateideskriptoren zu schließen. Soweit ich weiß, werden diese Dateien automatisch geschlossen, wenn der Prozess stirbt.

  • Gestalten Sie Ihr System so, dass immer die Möglichkeit besteht, ein sauberes Herunterfahren durchzuführen. Letzteres beinhaltet das Freigeben/Entsperren aller Ressourcen, die noch von dem System verwendet werden, das kurz vor dem Ausfall steht.

    – alk

    6. März 2013 um 12:07 Uhr


  • Wie der Import zu Recht sagt, ist explizit immer besser als implizit!!

    – Kaunteja

    6. März 2013 um 12:12 Uhr

  • @alk Dies ist ein kompliziertes Legacy-System, kaskadierende Fehler sind nicht möglich und Refactoring ist keine Option.

    – Standardruf

    6. März 2013 um 12:13 Uhr


  • C garantiert nur, dass alle Dateien geschlossen werden, wenn Sie entweder über die beenden exit Funktion oder eine Rückkehr von main.

    – teppisch

    6. März 2013 um 12:29 Uhr

  • @teppic also, wenn der Prozess wegen eines SIGABRT anhält, könnte ein Deskriptor vom Betriebssystem immer noch als “in Gebrauch” betrachtet werden?

    – Simone

    27. August 2013 um 8:17 Uhr

Benutzer-Avatar
David Ranieri

Dateien werden automatisch geschlossen, aber es ist eine bewährte Vorgehensweise.

Siehe valgrind zu diesem Beispiel

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)

Wie Sie sehen können, führt dies zu einem Speicherleck

Unter Umständen können Sie davon Gebrauch machen 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;
}

  • FILE Zeiger zurückgegeben von fopen() ist kein Dateideskriptor. Es ist ein Dateistrom, der einen zugeordneten Puffer hat. Also, wenn Ihr Programm zurückkehrt main ohne richtig fclose() Rufen Sie an, Sie lassen diese Erinnerung durchgesickert, wie Valgrind zeigt. Die Frage nach der Dateideskriptoren bleibt unbeantwortet.

    – Pawel Schuravlew

    6. März 2013 um 12:36 Uhr

Der klassische Leitfaden zur POSIX-Programmierung „Fortgeschrittene Programmierung in UNIX-Umgebung“ besagt:

Wenn ein Prozess beendet wird, werden alle geöffneten Dateien automatisch vom Kernel geschlossen. Viele Programme machen sich diese Tatsache zunutze und schließen geöffnete Dateien nicht explizit.

Sie haben das Betriebssystem in Ihrer Frage nicht erwähnt, aber ein solches Verhalten sollte von jedem Betriebssystem erwartet werden. Wann immer sich Ihr Programmkontrollfluss kreuzt exit() oder return aus main() Es liegt in der Verantwortung des Systems, nach dem Prozess aufzuräumen.

Es besteht immer die Gefahr von Fehlern bei der Betriebssystemimplementierung. Aber andererseits muss das System bei der Prozessbeendigung weit mehr als mehrere offene Dateideskriptoren freigeben: Speicher, der von dem ausführbaren Dateiimage belegt ist, Stack, Kernel-Objekte, die dem Prozess zugeordnet sind. Sie können dieses Verhalten nicht vom Benutzerbereich aus steuern, Sie verlassen sich einfach darauf, dass es wie beabsichtigt funktioniert. Warum kann sich ein Programmierer also nicht auf das automatische Schließen der verlassen fds?

Also das einzige Problem beim Verlassen fds offen kann die Frage des Programmierstils sein. Und wie im Fall der Verwendung stdio Objekte (dh Dinge, die um die vom System bereitgestellte Datei I/O herum gebaut wurden), erhalten Sie möglicherweise (etwas) verwirrende Warnungen, während Sie valgrinden. Was die Gefahr des Verlusts von Systemressourcen betrifft, sollten Sie sich keine Sorgen machen, es sei denn, Ihre Betriebssystemimplementierung ist wirklich fehlerhaft.

Soweit ich weiß, werden diese Dateien automatisch geschlossen, wenn der Prozess stirbt.

Verlassen Sie sich nicht darauf. Konzeptionell ist es so, wenn der Prozess stirbt Deine Verantwortung um zugewiesenen Speicher freizugeben, nicht standardmäßige Dateideskriptoren zu schließen usw. Natürlich wird jedes vernünftige Betriebssystem (und sogar Windows) nach Ihrem Prozess aufräumen, aber das ist nicht zu erwarten.

Ja. Angenommen, Ihr Hauptprogramm ist jetzt eine Klasse in einem separaten Programm. Jetzt haben Sie gerade ein Ressourcenleck beschrieben. Sie verletzen im Wesentlichen die Kapselung, indem Sie sich auf den globalen Programmstatus verlassen, dh den Status des Prozesses – nicht Ihr Modul, keine Klasse, kein ADT, kein Thread, sondern der gesamte Prozess – der sich in einem heruntergefahrenen Zustand befindet.

Jedes vernünftige Betriebssystem (sicherlich jede Form von Linux oder Windows) schließt die Dateien, wenn das Programm beendet wird. Wenn Sie ein sehr einfaches Programm haben, müssen Sie Dateien beim Beenden wahrscheinlich nicht schließen. Das explizite Schließen der Dateien ist jedoch aus folgenden Gründen immer noch eine bewährte Vorgehensweise:

  1. Wenn Sie es dem Betriebssystem überlassen, haben Sie keine Kontrolle über die Reihenfolge, in der die Dateien geschlossen werden, was zu Konsistenzproblemen führen kann (z. B. in einer Datenbank mit mehreren Dateien).

  2. Wenn beim Schließen der Datei Fehler auftreten (z. B. E/A-Fehler, Speicherplatzmangel usw.), haben Sie keine Möglichkeit, diese zu melden.

  3. Möglicherweise gibt es Wechselwirkungen mit Dateisperren, die behandelt werden müssen.

  4. Eine Routine zum Schließen aller Dateien kann jede andere Bereinigung durchführen, die das Programm gleichzeitig benötigt (z. B. das Leeren von Puffern).

Benutzer-Avatar
teppisch

C garantiert, dass alle geöffneten Dateien geschlossen werden, wenn Ihr Programm normal beendet wird (z. B. via exit oder eine Rücksendung von main). Wenn Ihr Programm jedoch abnormal beendet wird, z. B. wenn es vom Betriebssystem aufgrund der Verwendung eines NULL-Zeigers geschlossen wird, ist es Sache des Betriebssystems, die Dateien zu schließen. Daher ist es eine gute Idee sicherzustellen, dass Dateien geschlossen werden, wenn sie im Falle einer unerwarteten Beendigung nicht mehr benötigt werden.

Der andere Grund sind Ressourcenbeschränkungen. Die meisten Betriebssysteme haben Beschränkungen für die Anzahl der geöffneten Dateien (sowie viele andere Dinge), und daher empfiehlt es sich, diese Ressourcen zurückzugeben, sobald sie nicht mehr benötigt werden. Wenn jedes Programm alle seine Dateien auf unbestimmte Zeit offen halten würde, könnten Systeme ziemlich schnell in Probleme geraten.

1374170cookie-checkist es eine gute Praxis, Dateideskriptoren beim Beenden zu schließen

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy