web-dev-qa-db-ja.com

終了時にファイル記述子を閉じることをお勧めしますか

何らかの理由でプログラムに致命的な状況が発生した場合、エラーコードで終了したいと思います。致命的なエラーのコンテキストが他のファイル記述子の範囲外である場合があります。これらのファイル記述子を閉じることは良い習慣です。私の知る限り、これらのファイルはプロセスが終了すると自動的に閉じられます。

23
stdcall

ファイルは自動的に閉じられますが、それは良い習慣です。

この例のvalgrindを参照してください

_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)
_

ご覧のとおり、メモリリークが発生します

状況によっては、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;
}
_
16
David Ranieri

POSIXプログラミングの古典的なガイド「UNIX環境での高度なプログラミング」には次のように記載されています。

プロセスが終了すると、開いているすべてのファイルがカーネルによって自動的に閉じられます。多くのプログラムはこの事実を利用しており、開いているファイルを明示的に閉じません。

質問でOSについて言及していませんが、そのような動作はどのOSからも予想されるはずです。プログラム制御フローがexit()からmain()またはreturnを横切るときはいつでも、プロセス後にクリーンアップするのはシステムの責任です。

OSの実装には常にバグの危険性があります。ただし、その一方で、システムは、プロセスの終了時に、複数の開いているファイル記述子(プロセスに関連付けられた実行可能ファイルイメージ、スタック、カーネルオブジェクトによって占有されているメモリ)の割り当てを解除する必要があります。ユーザースペースからこの動作を制御することはできません。意図したとおりに機能することに依存しているだけです。では、なぜプログラマーはfdsの自動クローズに頼ることができないのでしょうか?

したがって、fdsを開いたままにしておくことの唯一の問題は、プログラミングスタイルの問題かもしれません。また、stdioオブジェクト(つまり、システムが提供するファイルI/Oを中心に構築されたもの)を使用する場合と同様に、valgrinding中に(ある程度)方向感覚を失うアラートが表示される場合があります。システムリソースがリークする危険性については、OSの実装に本当にバグがない限り、心配する必要はありません。

12
Pavel Zhuravlev

私の知る限り、これらのファイルはプロセスが終了すると自動的に閉じられます。

それに頼らないでください。概念的には、プロセスが終了すると、割り当てられたメモリを解放したり、非標準のファイル記述子を閉じたりするのはあなたの責任です。もちろん、すべての正常なOS(およびWindows)はプロセス後にクリーンアップします。しかし、それは期待することではありません。

7
user529758

はい。メインプログラムが別のプログラムのクラスになったとします。これで、リソースリークについて説明しました。グローバルプログラムの状態、つまりプロセスの状態(モジュール、クラス、ADT、スレッドではなく、プロセス全体)がシャットダウン状態にあることに依存することで、本質的にカプセル化に違反しています。

3
djechlin

すべての正常なオペレーティングシステム(確かにあらゆる形式のLinuxまたはWindows)は、プログラムの終了時にファイルを閉じます。非常に単純なプログラムを使用している場合は、終了時にファイルを閉じる必要はおそらくありません。ただし、次の理由により、ファイルを明示的に閉じることは依然として良い習慣です。

  1. oSに任せると、ファイルを閉じる順序を制御できなくなり、整合性の問題が発生する可能性があります(複数ファイルデータベースなど)。

  2. ファイルのクローズに関連するエラー(I/Oエラー、スペース不足エラーなど)がある場合、それらを報告する方法はありません。

  3. 処理する必要があるファイルロックとの相互作用があるかもしれません。

  4. すべてのファイルを閉じるルーチンは、プログラムが同時に必要とする他のクリーンアップを処理できます(たとえば、バッファのフラッシュ)

2
John Vincent

Cは、プログラムが正常に終了した場合(つまり、exitまたはmainからの戻り)に、開いているすべてのファイルが閉じられることを保証します。ただし、プログラムが異常終了した場合、たとえばNULLポインタを使用しているため、オペレーティングシステムによって閉じられます。ファイルを閉じるのは、オペレーティングシステム次第です。したがって、予期しない終了が発生した場合に備えて、ファイルが不要になったらファイルを閉じることをお勧めします。

もう1つの理由は、リソースの制限です。ほとんどのオペレーティングシステムでは、開くファイルの数(および他の多くのもの)に制限があるため、それらのリソースが不要になったらすぐに返すことをお勧めします。すべてのプログラムがすべてのファイルを無期限に開いたままにしておくと、システムで問題がすぐに発生する可能性があります。

1
teppic