【问题标题】:Size of buffered input in CC中缓冲输入的大小
【发布时间】:2021-10-08 20:24:24
【问题描述】:

我正在编写一个简单的 GNU coreutils 克隆,以便更好地理解 GNU/Linux 系统和 POSIX 系统。在我的“猫”克隆中,我使用fseek()ftell() 计算输入缓冲区size 的大小(不支持stdin),并将其分配给缓冲区。然后,我将fread()size 字节从流fp 到缓冲区buff,我将打印到stdout。 这是一段sn-p代码:

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

int main() {
    FILE* stream = fopen("file.txt", "r");
    fseek(stream, 0L, SEEK_END);
    long sz = ftell(stream);
    rewind(stream);
    char* buf = malloc(sz);
    fread(buf, sz, 1, stream);
    printf("%s", buf);
}

这个实现有两个主要问题:

  1. 如果流的字节大小大于用于保存它的整数数据类型的最大值(intsize_t),则该值将溢出,您将得到一个不正确的数字。在大多数情况下,这是通过使用size_t 而不是int 来解决的,因为size_t 是64 位的,因此可以处理更大的数字——但最终,它无法处理任意大小的文件而不会崩溃和烧毁。
  2. 对于非常大的文件(数 GB),分配的内存会太大,内核会发送 SIGKILL 以释放大量资源(在我的情况下)。这意味着该程序将完全无法处理非常大的文件并且效率不高。

我宁愿不使用 mmap,因为它增加了程序的复杂性——我试图让程序尽可能简单。我决定缓冲输入,即。 fread() x 字节,将它们打印到 stdout,释放内存并冲洗并重复,直到打印整个文件。

我的问题是,x 应该有多大?是否有应该读取多少字节的标准?数字太大,程序会变得非常占用内存,数字太小,程序会调用 fread() 太多次,从而降低整体速度。一次读取多少字节才能在速度和资源使用方面取得最佳折衷?

【问题讨论】:

  • 我会说这取决于你正在运行的机器......
  • 欢迎来到 SO。 ftell 返回 long,而不是 int。此外,如果您考虑cat,您可能正在处理文本文件。请注意,如果您以文本模式打开文件,\n 可能会转换为 \r\n
  • 当您确实需要查找磁盘文件的大小时(虽然在这里您确实不需要需要这样做!),并且如果您不担心可移植性对于非 Unixy 系统,那么 statfstat 通常是更好的选择。 (请参阅st_size 字段。)
  • 您几乎不需要知道文件的大小。如果你认为你这样做了,再想一想。
  • 在Linux上也有copy_file_range()

标签: c linux posix cat


【解决方案1】:

in rome 构建coreutils 克隆时,按照罗马人 GNU coreutils 的做法(或至少看看他们是如何做到的)。

According to this,GNU cat 使用的块大小由stat()/fstat()but at minumum 128 kiB报告的输入文件句柄和输出文件句柄的最大块大小定义(这个值,根据cmets,基于基准)。

【讨论】:

  • GNU cat 使用的块大小... cat 是一个非常有限的用例 - 流过文件内容一次。将这么大的缓冲区用于通用的FILE 缓冲区会导致严重的读取放大——每次调用fseek() 时,下一次读取操作都会读取数据以填满整个缓冲区。
【解决方案2】:

两件事 - 底层 *nix 文件系统将使用一些非常复杂的缓冲,所以在你的实现中过于聪明是没有意义的。

只需分配一个大小合适的缓冲区(4K 的倍数)并通过循环“fread”和“print”来继续重用它。 您将捕获 fread 实际读取的字节数,因为最后一次读取将小于缓冲区。

【讨论】:

    猜你喜欢
    • 2013-12-19
    • 1970-01-01
    • 1970-01-01
    • 2011-11-26
    • 2017-04-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-12-30
    相关资源
    最近更新 更多