新旧长度之间的流内容为 未定义
但是this blog post说:
最基本的方法是使用SetFilePointer移动 指向文件中较大位置的指针(不存在 ),然后使用SetEndOfFile将文件扩展到该大小。这个 文件已分配了磁盘空间,但NTFS实际上并未填充 零字节。它将按需懒惰地执行该操作。
它说NTFS延迟地用零填充字节。
我想FileStream.SetLength
在幕后叫SetFilePointer
和SetEndOfFile
。
更新: 博客文章还说:
奖金聊天:SetEndOfFile的文档说:“如果 文件扩展后,文件的内容在旧端之间 文件和文件的新结尾未定义。”但是我只是说 它将按需填充为零。谁是对的?
SetEndOfFile函数的正式定义是 扩展内容未定义。但是,NTFS将确保您 出于安全原因,永远都不会看到别人的剩余数据。 (假设您不是故意使用来绕过安全性 设置文件有效数据。)
其他文件系统可能会选择不同的行为
因此,似乎在NTFS系统上可以保证文件归零。
END UPDATE
SetFileValidData文档说:
SetFileValidData函数使您避免使用以下命令填充数据 不连续写入文件时为零
似乎是说,如果您不调用SetFileValidData
,则该文件将填充零。
所以问题是:
新旧长度之间的流的内容是否真的不确定,还是为零?
上下文:Windows 10,NTFS,Net Framework 4.72。
我还想问一下coreclr Linux和其他文件系统。
如果您不致电SetFileValidData
,在这种情况下内容可能非零?
我之所以问是因为我正在开发PersistentHashing依赖零填充的开源项目。
我编写了一个寻找非零值的小程序,但从未找到一个:
using System;
using System.IO;
using System.IO.MemoryMappedFiles;
using System.Threading;
using System.Threading.Tasks;
namespace TestZeros
{
class Program
{
static async Task Main(string[] args)
{
var cs = new CancellationTokenSource();
Console.WriteLine("Searching non zeros ...");
Console.WriteLine("Press enter to exit");
try
{
var testTask = Test(cs.Token);
var readLineTask = Task.Run(() =>
{
Console.ReadLine();
cs.Cancel();
});
long nonZeroValue = await testTask;
Console.WriteLine($"Found {nonZeroValue} in the file");
}
catch (OperationCanceledException)
{
Console.WriteLine("Non zero values couldn't be found");
}
catch (Exception ex)
{
Console.WriteLine("Something went wrong: \n" + ex.ToString());
}
}
static unsafe Task<long> Test(CancellationToken cancellationToken)
{
return Task.Run(() =>
{
while (true)
{
const int OneGigabyte = 1024 * 1024 * 1024;
string filePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "mmap.bin");
// create mmap file and accessor, then adquire pointer
var fileStream = new FileStream(filePath, FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite);
fileStream.SetLength(OneGigabyte);
var mmap = MemoryMappedFile.CreateFromFile(fileStream, null, fileStream.Length,
MemoryMappedFileAccess.ReadWrite,
null, HandleInheritability.None, true);
var accessor = mmap.CreateViewAccessor(0, 0, MemoryMappedFileAccess.ReadWrite);
byte* baseAddress = null;
accessor.SafeMemoryMappedViewHandle.AcquirePointer(ref baseAddress);
byte* endAddress = baseAddress + OneGigabyte;
try
{
long* pointer = (long*)baseAddress;
while (pointer < endAddress)
{
if (cancellationToken.IsCancellationRequested) throw new OperationCanceledException();
if (*pointer != 0) return *pointer;
pointer++;
}
pointer = (long*)baseAddress;
while (pointer < endAddress)
{
if (cancellationToken.IsCancellationRequested) throw new OperationCanceledException();
*pointer = -1L;
pointer++;
}
accessor.Flush();
}
finally
{
accessor.SafeMemoryMappedViewHandle.ReleasePointer();
accessor.SafeMemoryMappedViewHandle.Close();
accessor.Dispose();
mmap.SafeMemoryMappedFileHandle.Close();
mmap.Dispose();
fileStream.Dispose();
}
}
});
}
}
}