假设您有一个正在运行的进程,在一段时间内写入文件。在资源管理器进程中,突出显示该文件将显示“当前大小”。随后按 F5 键将更新该文件的当前大小,但令人惊讶的是,需要按两次键才能查看最正确的大小。按一次 F5 键只会得到更高的值。
这个问题已经困扰我好几年了,我想知道为什么!
它可以在(至少)Windows 7 中重现,例如通过下载大文件。
答案1
有一个MSDN 博客描述 Windows 为何会按照您所描述的方式运行。
首先要说的是,您所看到的仅限于 NTFS。
为了测试您所说的内容,我编写了一个小程序,每 5 秒向文件写入 40 kB。每次写入之间文件都保持打开状态。第二个程序用于FindFirstFileEx
获取当前文件大小。第三个程序我dir
在 cmd.exe 中使用。通过此设置,我可以准确看到您描述的内容。
导致此问题的原因是 NTFS 的设计决策。在 NTFS(如 Unix 文件系统)中,同一个文件可以位于两个目录中 - 这称为硬链接。这意味着您有两个目录,每个目录都有一个文件条目,并且您拥有文件本身及其属性。文件大小是属于文件的属性,因此它存储在那里。但是,如果有人想要目录中具有文件大小等属性的文件列表,那么如果您不仅要读取目录本身,还要读取每个文件的信息,那么您的性能将非常低。一个目录的数据可能按顺序存储,但不同文件的数据可能分散在整个磁盘上。因此,NTFS 将文件大小的副本存储在目录条目中。
您可能已经猜到了,这也会对性能造成影响。想象一下指向同一个文件的 10 个硬链接。每次写入文件时,您是否希望 NTFS 更新 10 个目录条目?不。因此,自 Vista 以来,我们做出了第二个设计决策:目录条目中的数据仅在文件关闭时更新。
您可以轻松检查:运行一个写入文件并保持文件打开的程序。运行dir
后您将看不到更新的大小。或者使用记事本写入文件(最后会关闭文件),新的文件大小会立即显示在dir
或 Explorer 中。
这怎么能F5帮助刷新文件大小呢?Explorer 调用获取命名安全信息它内部打开文件并关闭它(你可以检查SysInternals 进程监视器)。如果我调用GetNamedSecurityInfo
自己的程序,然后调用,FindFirstFileEx
我会立即看到新的文件大小。因此观察到的行为与理论预期完全一致。
但是为什么你没有在 Explorer 中立即看到新的文件大小?似乎 Explorer 先调用FindFirstFileEx
,然后GetNamedSecurityInfo
。因此 Explorer 获取旧大小,然后触发更新目录条目。如果你dir
在 cmd.exe 中运行,你会看到目录条目现在具有新的文件大小。只是 Explorer 还不知道。Explorer 需要一秒钟F5才能获取最新大小,然后再次触发更新。
从应用程序开发人员的角度来看,我不会认为这是 Explorer 错误 - 这是受支持的文件系统之一的特殊情况,应用程序应该从文件系统中抽象出来。但由于 Explorer 是 Windows 的一部分,我倾向于认为 Microsoft 可以做得更好,并改变函数调用的顺序以获得更好的用户体验。
顺便说一句,谢谢你这个非常有趣的问题!我喜欢学习这样的 NTFS 内部知识。