【问题标题】:is it a good practice to close file descriptors on exit在退出时关闭文件描述符是一个好习惯吗
【发布时间】:2013-03-06 12:01:11
【问题描述】:

如果由于某种原因,我在我的程序中发现了一个致命的情况,我想以错误代码退出。 有时,致命错误的上下文超出了其他文件描述符的范围。 关闭这些文件描述符是一个好习惯吗? 据我所知,这些文件会在进程终止时自动关闭。

【问题讨论】:

  • 以这样一种方式设计您的系统,即始终有可能执行干净的关机。后者包括释放/解锁任何仍在被系统使用且即将停机的资源。
  • 作为 import 这正确地说显式总是比隐式好!!
  • @alk 这是一个复杂的遗留系统,不可能出现级联错误,也不能进行重构。
  • C 仅保证在您通过exit 函数退出或从main 返回时关闭所有文件。
  • @teppic 因此,如果进程由于 SIGABRT 而停止,某些描述符仍可能被操作系统视为“正在使用”?

标签: c coding-style file-descriptor


【解决方案1】:

文件会自动关闭,但这是一个好习惯。

在这个例子中查看 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;
}

【讨论】:

  • FILEfopen() 返回的指针不是文件描述符。它是一个文件流,具有关联的缓冲区。因此,当您的程序从 main 返回而没有正确的 fclose() 调用时,您会将此内存泄漏,如 valgrind 所示。关于文件描述符的问题仍未得到解答。
【解决方案2】:

POSIX 编程的经典指南“UNIX 环境中的高级编程”指出:

当一个进程终止时,其所有打开的文件都会被内核自动关闭。许多程序利用了这一事实,并没有明确关闭打开的文件。

您没有在问题中提及操作系统,但任何操作系统都应该出现这种行为。每当您的程序控制流从main() 跨越exit()return 时,系统负责在处理后进行清理。

在操作系统实现中总是存在错误的危险。但是,另一方面,系统必须在进程终止时释放多个打开的文件描述符:可执行文件映像、堆栈、与进程关联的内核对象占用的内存。您无法从用户空间控制此行为,您只能依赖其按预期工作。那么,程序员为什么不能依赖fds 的自动关闭呢?

因此,让fds 保持打开状态的唯一问题可能是编程风格问题。并且,就像使用 stdio 对象(即围绕系统提供的文件 i/o 构建的东西)一样,您可能会在 valgrinding 时收到(有点)迷失方向的警报。至于泄露系统资源的危险,应该没什么好担心的,除非你的操作系统实现真的有问题。

【讨论】:

    【解决方案3】:

    据我所知,这些文件在进程终止时会自动关闭。

    不要依赖它。从概念上讲,当进程终止时,您有责任释放分配的内存、关闭非标准文件描述符等。当然,每个健全的操作系统(甚至 Windows)都会在您的进程之后清理,但这并不值得期待。

    【讨论】:

      【解决方案4】:

      是的。假设您的主程序现在是单独程序中的一个类。现在您刚刚描述了资源泄漏。您依赖全局程序状态(即进程的状态)实质上违反了封装 - 不是您的模块,不是类,不是 ADT,不是线程,而是整个进程 - 处于关闭状态。

      【讨论】:

        【解决方案5】:

        每个正常的操作系统(当然是任何形式的 Linux 或 Windows)都会在程序终止时关闭文件。如果您有一个非常简单的程序,那么您可能不需要在终止时关闭文件。然而,明确关闭文件仍然是一种很好的做法,原因如下:

        1. 如果您将其留给操作系统,您将无法控制文件关闭的顺序,这可能会导致一致性问题(例如在多文件数据库中)。

          李>
        2. 如果出现与关闭文件相关的错误(例如 I/O 错误、空间不足错误等),您将无法报告。

        3. 可能存在需要处理的文件锁定交互。

        4. 关闭所有文件的例程可以同时处理程序需要的任何其他清理(例如刷新缓冲区)

        【讨论】:

          【解决方案6】:

          C 确实保证如果您的程序正常终止(即通过exit 或从main 返回),所有打开的文件都将被关闭。但是,如果您的程序异常终止,例如由于使用 NULL 指针,它被操作系统关闭,关闭文件由操作系统决定。因此,最好确保文件在不再需要时关闭,以防意外终止。

          另一个原因是资源限制。大多数操作系统对打开的文件数量(以及许多其他内容)都有限制,因此最好在不再需要这些资源时立即返回它们。如果每个程序都无限期地打开所有文件,系统可能很快就会遇到问题。

          【讨论】:

            猜你喜欢
            • 2020-08-25
            • 1970-01-01
            • 2015-08-03
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2010-09-21
            • 2023-01-06
            相关资源
            最近更新 更多