- 让我们拥有任何二进制数据流(如
/dev/random
、/dev/zero
等)。 - 我们有一个文件,其大小最大为 N。
- N 的单位是千兆字节。
有没有任何优雅的Linux中的方法/技术使用/黑客,实现这样的文件连续写入的长度最大为 N 个字节,并且始终仅包含最后写入的数据(按顺序)?这意味着没有大的移动(文件到文件,内存到文件),只需对最后/第一个数据块进行少量调整。
我正在寻找的技巧是文件开始向前移动并有效地忘记任何太旧的内容(这会增加文件大小超过 N) - 内容轮换。
期望的原则可以表示为:
inp = fopen(stream, "r");
out = fopen(file, "wN"); // "Special open" with maximal size of N
while (is_reading)
{
if (rd = fread(buff, block_size, 1, inp))
{
fwrite(buff, rd, 1, out); // Old content forgotten
fflush(out); // Allow others to instantly see the current content
}
}
fclose(inp);
fclose(out);
答案1
在 Linux 上,您可以使用fallocate()
取消分配文件开头的数据。
解除分配文件空间
在模式下指定 FALLOC_FL_PUNCH_HOLE 标志(自 Linux 2.6.38 起可用)会在从 offset 开始并持续 len 字节的字节范围中释放空间(即创建一个洞)。在指定范围内,部分文件系统块将被清零,整个文件系统块将从文件中删除。成功调用后,从此范围内的后续读取将返回零。
...
这不会改变ls
或stat
或类似报告的文件大小,但会减少实际磁盘使用量,因为文件将会稀疏。尝试从文件中的“漏洞”中读取仍然会成功,并将0
向读取过程返回 -filled 字节。
像这样的东西:
size_t maxSize = 512UL * 1024UL * 1024UL;
char buffer[ 8 * 1024 ];
struct stat sb;
int in = open( inputFile, O_RDONLY );
int out = open( outputFile, O_WRONLY | O_CREAT | O_APPEND, 0644 );
fstat( out, &sb );
size_t fileSize = sb.st_size;
for ( ;; )
{
ssize_t bytesRead = read( in, buffer, sizeof( buffer ) );
if ( bytesRead < 0 )
{
break;
}
ssize_t bytesWritten = write( out, buffer, bytesRead );
if ( bytesWritten < 0 )
{
break;
}
fileSize += bytesWritten;
if ( fileSize > maxSize )
{
fsync( out );
off_t endOfHole = fileSize - maxSize;
fallocate( out, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE,
0UL, endOfHole );
}
}
仅 XFS、BTRFS、EXT4 和 tmpfs 支持。
它还需要更多的错误检查,甚至可能无法按原样编译。它的效率也非常低,因为一旦达到最大大小,它将调用fallocate()
每个read()
/write()
周期,并且每次都会从文件的开头打“洞”。
对于这种 IO 模式使用缓冲fread()
/也是没有意义的。fwrite()
只是read()
/write()
足够大的块。