谁释放了 setvbuf 缓冲区?

发布于 2024-08-29 15:02:23 字数 1387 浏览 11 评论 0原文

因此,我一直在深入研究 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,则仅 模式受到影响;一个新的缓冲区将是 在下次读取或写入时分配 操作。

编辑:我的问题的附录。由于很明显,我必须释放传递给setvbuf的任何缓冲区,如果我实际上在stdout上使用它,有没有任何实用的方法要免费吗?它必须存活到程序结束。我能想到的最好的办法是 fclose(stdout) 然后释放它或使用静态缓冲区,正如一些人提到的那样。我问这个问题是因为这看起来确实是一个尴尬的设计决定。

So I've been digging into how the stdio portion of libc is implemented and I've come across another question. Looking at man setvbuf I see the following:

When the first I/O operation occurs on
a file, malloc(3) is called, and a
buffer is obtained.

This makes sense, your program should not have a malloc in it for I/O unless you actually use it. My gut reaction to this is that libc will clean up its own mess here. Which I can only assume it does because valgrind reports no memory leaks (they could of course do something dirty and not allocate it via malloc directly... but we'll assume that it literally uses malloc for now).

But, you can specify your own buffer too...

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

Oh no, memory leak! valgrind confirms it. So it seems that whenever stdio allocates a buffer on its own, it will get deleted automatically (at the latest on program exit, but perhaps on stream close). But if you specify the buffer explicitly, then you must clean it up yourself.

There is a catch though. The man page also says this:

You must make sure that the space that
buf points to still exists by the time
stream is closed, which also happens
at program termination. For example,
the following is invalid:

Now this is getting interesting for the standard streams. How would one properly clean up a manually allocated buffer for them, since they are closed in program termination? I could imagine a "clean this up when I close flag" inside the file struct, but it get hairy because if I read this right doing something like this:

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

would cause 2 allocations by the standard library because of this sentence:

If the argument buf is NULL, only the
mode is affected; a new buffer will be
allocated on the next read or write
operation.

EDIT: an addendum to my question. Since it is clear that I must free any buffers I pass to setvbuf, if I do in fact use it on stdout is there any practical way to free it? It must live to program end. The best I can think of is to fclose(stdout) then free it or use a static buffer as some people have mentioned. I ask because it does seem like an awkward design decision.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

栖竹 2024-09-05 15:02:23

同样来自 手册页(至少在我的系统上):

如果 buf 不为 NULL,则调用者有责任在关闭流后释放(3) 此缓冲区。

也就是说,你对它进行了 malloc,然后释放了它。

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

Also from the man page (at least, on my system):

If buf is not NULL, it is the caller's responsibility to free(3) this buffer after closing the stream.

That is, you malloc-ed it, you free it.

Before exit, you could close the streams yourself, thus allowing you to free the buffer. Alternatively, you could flush the streams and call setvbuf again with a NULL buffer argument to switch back to a library managed buffer or unbuffered I/O.

陌上芳菲 2024-09-05 15:02:23

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

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

At least according to the C standard, your last scenario simply isn't allowed: "The setvbuf function may be used only after the stream pointed to by stream has been associated with an open file and before any other operation (other than an unsuccessful call to setvbuf) is performed on the stream." (C99, §7.19.5.6/2).

As to when to free the memory in the simpler cases, on way is to call atexit() to register a callback that will free the memory after exiting from main(), but before control is returned to the OS.

等风来 2024-09-05 15:02:23

您可以显式关闭 stdinstdoutstderr(使用 fclose())。

对于大多数操作系统,堆内存会在程序退出时自动释放。因此,拥有未释放的缓冲区并没有实际问题(有一个表面问题,即那些未释放的缓冲区会污染 Valgrind 的输出)。如果您想在标准输入或输出上调用 setvbuf(),我的建议是使用静态缓冲区。静态缓冲区不需要分配或释放,并且在这里很合适,因为只有三个标准流,并且您担心这些流在程序终止时保持开放的情况。

You can close stdin, stdout and stderr explicitly (with fclose()).

With most operating systems, heap memory is automatically released upon program exit. So there is no practical issue with having unreleased buffers (there is a cosmetic issue, which is that those unreleased buffers pollute Valgrind's output). My advice would be to use static buffers if you feel the urge to call setvbuf() on standard input or output. Static buffers needs not be allocated or released, and are appropriate here since there are only three standard streams and you are worrying about a situation where those streams are kept open up to the program termination.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文