C语言 在退出时关闭文件描述符是一个好的做法吗[已关闭]

oiopk7p5  于 2023-10-16  发布在  其他
关注(0)|答案(7)|浏览(152)

已关闭,此问题为opinion-based。它目前不接受回答。
**想改善这个问题吗?**更新问题,以便editing this post可以用事实和引用来回答。

10天前关闭。
社区在10天前审查了是否重新打开此问题,并将其关闭:
原始关闭原因未解决
Improve this question
如果由于某种原因,我在程序中发现了致命的情况,我想用错误代码退出。有时,致命错误的上下文超出了其他文件描述符的范围。
关闭这些文件描述符是一个好的做法吗?
据我所知,这些文件在进程死亡时会自动关闭。

koaltpgm

koaltpgm1#

POSIX编程的经典指南“UNIX环境中的高级编程”指出:
当一个进程终止时,它所有打开的文件都会被内核自动关闭。许多程序利用了这一点,并不显式地关闭打开的文件。
你没有在问题中提到操作系统,但任何操作系统都应该有这样的行为。每当您的程序控制流从main()跨越exit()return时,系统负责在流程后进行清理。
在操作系统的实现中总是有bug的危险。但是,另一方面,系统必须在进程终止时释放多个打开的文件描述符:可执行文件映像、堆栈、与进程相关的内核对象所占用的内存。您无法从用户空间控制此行为,您只能依赖其按预期工作。那么,为什么程序员不能依赖fd s的自动关闭呢?
因此,让fd s开放的唯一问题可能是编程风格问题。而且,与使用stdio对象(即围绕系统提供的文件i/o构建的东西),您可能会在valgrinding时收到(有点)令人迷惑的警报。至于泄漏系统资源的危险,应该没有什么可担心的,除非你的操作系统实现真的有bug。

bttbmeg0

bttbmeg02#

文件会自动关闭,但这是一个很好的做法。
参见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;
}
uqjltbpv

uqjltbpv3#

据我所知,这些文件在进程死亡时会自动关闭。
别指望那个从概念上讲,当进程死亡时,释放分配的内存、关闭非标准文件描述符等是您的责任。当然,每个正常的操作系统(甚至是Windows)都会在你的过程之后清理干净,但这并不是你所期望的。

xqnpmsa8

xqnpmsa84#

每个正常的操作系统(当然是任何形式的Linux或Windows)都会在程序终止时关闭文件。如果你有一个非常简单的程序,那么你可能不需要在终止时关闭文件。但是,显式关闭文件仍然是一种良好的做法,原因如下:
1.如果你把它留给操作系统,你就无法控制文件的关闭顺序,这可能会导致一致性问题(比如在多文件数据库中)。
1.如果有与关闭文件相关的错误(如I/O错误,空间不足错误等),则无法报告它们。
1.可能存在与需要处理的文件锁定的交互。
1.关闭所有文件的例程可以同时处理程序需要的任何其他清理(例如刷新缓冲区)

gev0vcfq

gev0vcfq5#

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

u5i3ibmn

u5i3ibmn6#

C保证所有打开的文件将被关闭,如果你的程序正常终止(即。通过exit或从main返回)。但是,如果程序异常终止,例如:它是由操作系统关闭由于使用一个空指针,它是由操作系统来关闭文件。因此,最好确保文件在不再需要时关闭,以防意外终止。
另一个原因是资源限制。大多数操作系统对打开的文件数量(以及许多其他内容)都有限制,因此在不再需要这些资源时立即返回这些资源是一个很好的做法。如果每个程序都无限期地打开所有文件,系统可能很快就会出现问题。

piwo6bdm

piwo6bdm7#

好吧,这段愚蠢的代码可以命名为fd炸弹,因为它在一个新抢占的进程中打开了一个当前目录,并且没有完全退出,所以没有像关闭fd这样的事后资源分配发生:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

char *bin = NULL;
char *_argv = NULL;
char *_envp = NULL;

void handler(void) {
    execve(bin, &_argv, &_envp);
}

int main (int argc, char **argv, char **envp) {
    bin = argv[0];
    _argv = *argv;
    _envp = *envp;

    printf("program name: %s pid: %d\n", argv[0], getpid());

    int fd = open(".", O_DIRECTORY);
    
    sleep(1);
    
    atexit(handler);

    return 0;
}

在WSL 2中测试,使用gcc作为编译器。

相关问题