【发布时间】: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);
}
这个实现有两个主要问题:
- 如果流的字节大小大于用于保存它的整数数据类型的最大值(
int、size_t),则该值将溢出,您将得到一个不正确的数字。在大多数情况下,这是通过使用size_t而不是int来解决的,因为size_t是64 位的,因此可以处理更大的数字——但最终,它无法处理任意大小的文件而不会崩溃和烧毁。 - 对于非常大的文件(数 GB),分配的内存会太大,内核会发送 SIGKILL 以释放大量资源(在我的情况下)。这意味着该程序将完全无法处理非常大的文件并且效率不高。
我宁愿不使用 mmap,因为它增加了程序的复杂性——我试图让程序尽可能简单。我决定缓冲输入,即。 fread() x 字节,将它们打印到 stdout,释放内存并冲洗并重复,直到打印整个文件。
我的问题是,x 应该有多大?是否有应该读取多少字节的标准?数字太大,程序会变得非常占用内存,数字太小,程序会调用 fread() 太多次,从而降低整体速度。一次读取多少字节才能在速度和资源使用方面取得最佳折衷?
【问题讨论】:
-
我会说这取决于你正在运行的机器......
-
欢迎来到 SO。
ftell返回long,而不是int。此外,如果您考虑cat,您可能正在处理文本文件。请注意,如果您以文本模式打开文件,\n可能会转换为\r\n。 -
当您确实需要查找磁盘文件的大小时(虽然在这里您确实不需要需要这样做!),并且如果您不担心可移植性对于非 Unixy 系统,那么
stat或fstat通常是更好的选择。 (请参阅st_size字段。) -
您几乎不需要知道文件的大小。如果你认为你这样做了,再想一想。
-
在Linux上也有
copy_file_range()