谁释放setvbuf缓冲区?

8

我一直在研究libc的stdio部分是如何实现的,然后我遇到了另一个问题。看着man setvbuf,我看到了以下内容:

当文件上发生第一次I/O操作时,会调用malloc(3)并获取缓冲区。

这很有意义,除非你真正使用它,否则你的程序不应该在I/O中使用malloc。我的第一反应是libc会自己清理这个混乱。我只能假设它确实这样做,因为valgrind没有报告任何内存泄漏(当然,他们可能会做一些肮脏的事情,并不直接通过malloc分配它...但我们现在假设它确实使用malloc)。

但是,你也可以指定自己的缓冲区...

int main() {
    char *p = malloc(100);
    setvbuf(stdio, p, _IOFBF, 100);
    puts("hello world");
}

哦,不好了,内存泄漏! valgrind确认了这一点。因此,每当stdio自己分配缓冲区时,它就会被自动删除(最迟在程序退出时,但可能在流关闭时)。但是,如果您明确指定缓冲区,则必须自行清理。

然而,有一个问题。手册还说:

您必须确保buf指向的空间仍然存在于流关闭时,这也发生在程序终止时。例如,以下内容无效:

现在对于标准流来说变得更加有趣了。如果要正确清除为其手动分配的缓冲区,该怎么办,因为它们在程序终止时关闭?我可以想象在文件结构中有一个“在我关闭时清理这个”的标志,但这会变得麻烦,因为如果我正确地阅读了这个标志,做类似这样的事情:

setvbuf(stdout, 0, _IOFBF, 0);
printf("hello ");
setvbuf(stdout, 0, _IOLBF, 0);
printf("world\n");

这句话会导致标准库进行2次分配:

如果参数buf为NULL,则仅影响模式;新缓冲区将在下一次读取或写入操作时分配。

编辑: 对我的问题补充说明。既然我必须free任何我传递给setvbuf的缓冲区,如果我确实在stdout上使用它,有没有实际的方法可以free它?它必须存活到程序结束。我能想到的最好的办法是fclose(stdout)然后释放它或者像一些人提到的那样使用静态缓冲区。我问这个问题是因为它似乎是一个笨拙的设计决策。
3个回答

3

来自man page(至少在我的系统上):

如果buf不是NULL,则由调用者负责在关闭流之后释放此缓冲区。

也就是说,你malloc它,你free它。

在退出前,您可以自行关闭流,从而允许您释放缓冲区。或者,您可以刷新流并再次使用NULL缓冲区参数调用setvbuf以切换回库管理的缓冲区或未缓冲的I/O。


更新答案以链接到页面。它是OS X,但man页面来自FreeBSD(http://www.freebsd.org/cgi/man.cgi?query=setvbuf)。 - outis
很难想象事情会有所不同,因为可以提供一个静态缓冲区,该缓冲区不得使用free()释放。 - anon
@Neil:确实,我更在想stdlib是否存在内存泄漏设计,因为没有办法从流中获取它分配的缓冲区。 - Evan Teran
3
根据我的回答中引用的标准,不允许再次使用 NULL 缓冲区参数调用 "setvbuf"。 - Jerry Coffin

3

至少根据C标准,您最后的场景是不允许的:“只有在流与打开的文件相关联并且在流上执行任何其他操作(除了对setvbuf的不成功调用)之前,才可以使用setvbuf函数。”(C99,§7.19.5.6/2)。

至于在简单情况下何时释放内存,一种方法是调用atexit()注册回调函数,在从main()退出但在控制返回给操作系统之前释放内存。


有趣。所以,我的最后一个例子属于未定义行为的类别。 - Evan Teran

1

你可以使用 fclose() 显式地关闭 stdinstdoutstderr

在大多数操作系统中,在程序退出时,堆内存会自动释放。因此,拥有未释放的缓冲区并没有实际问题(存在一个美观的问题,即这些未释放的缓冲区会污染 Valgrind 的输出)。如果你想要调用 setvbuf() 来处理标准输入或输出流,我的建议是使用静态缓冲区。静态缓冲区不需要分配或释放,并且在这里是适当的,因为只有三个标准流,而你担心的是这些流在程序终止之前一直保持打开状态。


污染的调试输出是一个问题,因为你习惯于忽略输出,很容易错过真正的错误。 - Mark Ransom
@Thomas:在内存被释放之前,文件是否有关闭的保证? - outis
@outis:在自动释放内存的操作系统中,内存是“原子性”释放的,在执行线程停止存在的点上。在此之后没有任何可能运行的东西,特别是没有libc代码可以(错误地)访问已释放的缓冲区。简而言之,它“只是工作”。仍然可能存在一些打开的文件和缓冲数据,但在应用程序代码无法触及的操作系统内核之外。 - Thomas Pornin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接