在 Windows Server 上,日志记录工具会持续将 .log 文件写入磁盘,并每天午夜轮换日志文件。每天都会创建新的日志文件,但几乎每天其内容/元数据都不会在 Windows 资源管理器和 Powershell 中更新。
示例:一个文件创建于 2022 年 5 月 17 日 0:00。它在 Windows 资源管理器中的大小约为 24KB,最后写入时间约为午夜(我没有检查确切时间到秒)。如果我使用 Powershell
(Get-Item).Length
我得到的尺寸是 24401。
现在,当我在 Windows 资源管理器中右键单击同一个文件并检查其属性时,它会将大小更新为 4,593KB,并将最后写入时间更新为 2022 年 5 月 17 日 09:34。确切的时间和大小并不重要,但问题是:为什么 Windows 资源管理器和 Windows Powershell 与底层文件系统完全不同步?什么甚至会导致这样的差异?
还有一次我用它来测试文件复制时是否不同步或内容是否正确:
Copy-Item ".. source file .." -Destination ".. somewhere .."
原始文件的属性得到更新,因此我可以将其用作一个非常糟糕的解决方法,以便始终在 Powershell 中实际获得该文件的最新版本,因为我正在使用 Powershell 脚本分析该文件,并且确实需要它的所有内容,而不仅仅是该文件在 9 小时前创建时所拥有的内容。
有人曾经在 Windows 中见过这种行为吗?
- 文件系统:NTFS
- 操作系统:Windows Server 2019 版本 1809 内部版本 17763.2803
- “硬件”:VMware ESXi Server 上的虚拟服务器
答案1
是的,这是因为该过程仍在写入文件并且缓冲区尚未刷新。
通常,当您想要写入文件时,您可以(大致)执行以下操作:
1. Create/Open a file and retrieve a "channel" (a handle) towards the file
2. Write something
3. Flush the buffer/Close the file "channel" (handle) (=commit to disk)
(then, repeat 1, 2 and 3 when you want to write something again)
但是,如果您想偶尔写一些东西,那么做这样的事情是可以的,因为从性能上讲,“打开文件句柄”和“刷新缓冲区”并不是“免费的”。
这意味着,如果您计划频繁写入文件,最好只是Create/Open a file
,然后Write
根据需要多次,完成后Flush
或关闭句柄(在我上面的例子中,这意味着1
, [根据需要2
重复步骤],然后,稍后, )2
3
当数据未被刷新到磁盘/提交时,您不能依赖它们!
微软提供了更多详细信息:
[...]Windows 将文件读写操作中的数据存储在系统维护的数据缓冲区中,以优化磁盘性能。当应用程序写入文件时,系统通常会缓冲数据并定期将数据写入磁盘[...]
这刷新文件缓冲区Windows API 文档说:
[...]通常,WriteFile 和 WriteFileEx 函数将数据写入内部缓冲区,操作系统会定期将该缓冲区写入磁盘或通信管道。FlushFileBuffers 函数将指定文件的所有缓冲信息写入设备或管道。
由于系统内的磁盘缓存交互,当许多写入操作分别执行时,在每次写入磁盘驱动设备后使用 FlushFileBuffers 函数可能会效率低下。[...]
创建文件文档:
[...]当应用程序使用完 CreateFile 返回的对象句柄后,请使用 CloseHandle 函数关闭该句柄。这不仅可以释放系统资源,还可以对共享文件或设备以及将数据提交到磁盘等产生更广泛的影响。本主题中会酌情注明具体细节。[...]
有关更多信息此处提供文件缓存, 注意:
文件系统元数据始终被缓存。因此,要将任何元数据更改存储到磁盘,必须刷新文件或使用 FILE_FLAG_WRITE_THROUGH 打开文件。
因此,正如您所看到的,行为取决于应用程序的设计,出于性能原因,日志文件仅偶尔刷新到磁盘的情况并不少见,并且您不能依赖元数据,例如日期/时间、文件大小,甚至在应用程序仍在写入文件时的文件内容。