setvbuf() - buf 为 NULL 时的缓冲区大小

setvbuf() - buffer size when buf is NULL

我已经阅读了 similar question 的答案,但我对执行 I/O 文件时的流缓冲更感兴趣。

当我运行下面的代码

#include <stdio.h>

int main(void) {

    printf("%d\n", BUFSIZ);

    FILE *fp = fopen("ekomi.txt", "wb");

    char array[2] = {'a', 'b'};
    fwrite(array, sizeof(char), 2, fp);
    fclose(fp);

    return 0;
}

它打印 BUFSIZ = 8192。 据我所知,BUFSIZ 应该是 glibc 使用的值,用于在 fopen 之后默认创建的缓冲区大小(据我所知,这应该与调用 setvbuf 将 NULL 指针传递给参数 buf 相同)。 我运行这个带有massif的程序,输出清楚地表明glibc分配的缓冲区是4096字节长

massif output

GNU C 库文档本身并不清楚缓冲区的默认大小:

If you specify a null pointer as the buf argument, then setvbuf allocates a buffer itself using malloc. This buffer will be freed when you close the stream.

所以我的问题是,glibc 如何决定为缓冲区分配多少内存?

简短回答:BUFSIZ 是允许的最大缓冲区大小,但 malloc 中使用的缓冲区大小由文件系统块大小给出。

长答案: 让我们回顾一下当我们让一个 NULL 指针作为 buf 传递时 setvbuf 做了什么。

https://sourceware.org/git/?p=glibc.git;a=blob;f=libio/iosetvbuf.c#l60

_IO_DOALLOCATE (fp)

https://sourceware.org/git/?p=glibc.git;a=blob;f=libio/libioP.h#l237

#define _IO_DOALLOCATE(FP) JUMP0 (__doallocate, FP)

这里它使用 JUMP0 宏调用与 FILE vtable 关联的“__doallocate”函数,它最终调用 _IO_file_doallocate() 定义在这里:

https://sourceware.org/git/?p=glibc.git;a=blob;f=libio/filedoalloc.c#l97

#if _IO_HAVE_ST_BLKSIZE
    if (st.st_blksize > 0 && st.st_blksize < _IO_BUFSIZ)
        size = st.st_blksize;
#endif

在上述行中,您可以看到我在简短回答中提到的内容。它使用 stat64 结构 (https://linux.die.net/man/2/stat64) 检索文件系统的块大小,如果它小于 BUFSIZ,则相应地设置大小。