【问题标题】:C - Proper way to close files when using both open() and fdopen()C - 同时使用 open() 和 fdopen() 时关闭文件的正确方法
【发布时间】:2012-12-03 20:26:57
【问题描述】:

所以我正在用 C 语言构建一个 Unix minishell,并正在实现输入、输出和错误重定向,但遇到了文件问题。我在找到重定向运算符的循环中打开我的文件,并使用 open(),它返回一个 fd。然后我相应地分配孩子的 fd,并调用执行函数。

当我的 shell 只是出去寻找程序并使用 execvp() 执行它们时,我没有太多问题。唯一的问题是在提示输入下一个命令行之前,我是否需要在文件描述符上调用 close()。我担心 fd 泄漏,但不完全了解它是如何工作的。

我真正的问题出现在使用内置命令时。我有一个名为“read”的内置命令,它接受一个参数,一个环境变量名称(可能是一个尚不存在的)。 Read 然后提示输入一个值,并将该值分配给变量。这是一个例子:

% read TESTVAR
test value test value test value
% echo  ${TESTVAR}
test value test value test value

好吧,可以说我尝试这样的事情:

% echo here's another test value > f1
% read TESTVAR < f1
% echo  ${TESTVAR}
here's another test value

这很好用,请记住 read 在父进程内执行,我不使用 execvp 调用 read,因为它是内置的。读取使用gets,它需要一个流变量,而不是fd。因此,在 irc 论坛上闲逛了一下之后,我被告知使用 fdopen,从文件描述符中获取流。所以在调用 get 之前,我调用:

rdStream = fdopen(inFD, "r");

然后调用

if(fgets(buffer, envValLen, rdStream) != buffer) 
{
    if(inFD) fclose(rdStream);
    return -1;
}
if(inFD) fclose(rdStream);

如您所见,目前我正在使用 fclose() 关闭流,除非它等于 stdin(即 0)。这是必要的吗?我需要关闭流吗?还是只是文件描述符?或两者?我很困惑我应该关闭哪个,因为它们都以不同的方式引用同一个文件。目前我没有关闭 fd,但我认为我绝对应该。我希望有人帮助确保我的 shell 没有泄漏任何文件,因为我希望它能够在单个会话中执行数千个命令而不会泄漏内存。

谢谢,如果你们想让我发布更多代码,请询问。

【问题讨论】:

    标签: c unix file-io memory-leaks


    【解决方案1】:

    standard 说:

    fclose() 函数应在 与指向的流相关联的文件描述符 流。

    所以调用fclose 就足够了;它也会关闭描述符。

    【讨论】:

    • 另外调用close会不好?在很多情况下,我认为文件描述符不会在命令内关闭,例如,如果我调用“echo abc > f1”,echo 不会关闭 f1 会吗?因此,每次使用重定向时,我都需要在我的 shell 中调用 close 。知道这种情况发生了,我还需要关闭流吗?我猜我这样做了,因为调用 fclose 需要刷新缓冲区等等。
    • @Scriptonaut 标准没有提到它。我的猜测是 close 将返回 -1 但从技术上讲,该行为是未定义的。因此,您可以设计一个方案,在fclose 之后,您还将关联的描述符设置为-1。然后close 将完全失败并使用EBADF。更好的是:不要将标准 I/O 与低级 I/O 混合使用。
    • 是的,我宁愿远离高级 IO,因为我的 shell 主要依赖于低级的东西。是否有像 get 这样的函数使用 fd 而不是流?无论如何,感谢您的帮助,我想我基本上已经弄清楚了。
    • @Scriptonaut 不,不幸的是没有像fgets这样的功能
    • 另外调用close非常糟糕。这是一种双重释放错误。在没有信号处理程序的单线程程序中,你可能不会遇到麻烦,但如果在fcloseclose 之间有任何打开另一个文件的机会,你会不小心关闭一个不相关的文件并严重混乱提升程序的状态。这可能会导致危险的文件损坏错误。
    【解决方案2】:

    FILE 是标准 C 库中的缓冲对象。当您执行fclose(标准C 函数)时,它最终会调用close(Unix 系统函数),但只有在确保刷新C 库缓冲区之后。所以,我想说,如果你使用fopenfwrite,那么你应该使用fclose,而不仅仅是close,否则你可能会丢失一些数据。

    【讨论】:

    • 好的。我想我需要在 shell 的主循环中调用 close(),因为在很多情况下 cmd 本身不会在关闭文件后查看,例如“echo abc > f1”。知道我需要在主循环中调用 close() ,我猜我仍然需要在 read 中调用 fclose() ,因为 fclose 会像你说的那样做其他更高级别的事情。如果我不需要文件流来获取一行输入,这一切都会容易得多。谢谢。
    • 我认为调用fclose 一次就足够了。另外,请注意,当进程终止时,打开的文件会自动关闭(调用close)。
    • 如果我不使用 fdopen(),我不需要调用 close 吗?这在这个 shell 中经常会发生。我的 shell 不会为每个命令调用一个进程,有时它会直接在父进程中执行它。
    • 好的,在这种情况下,我认为您有两个选择:要么只使用 FILE 对象,要么同时使用文件描述符和 FILE,但要跟踪是什么并使用适当的函数来关闭它们。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-09-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-01-29
    • 2020-06-24
    • 2019-08-16
    相关资源
    最近更新 更多