偶尔我会遇到一些程序,根据文件系统,这些程序在写入过程中会写入长度为“0 字节”的文件,但写入会缩小驱动器报告的可用空间。例如,程序正在wget
下载一些大文件或ffmpeg
转换视频。
由于某些编码、下载或其他较慢的操作,写入时间很长,例如写入数百兆字节需要几分钟。这次我可以看到,根据文件系统,可用磁盘空间缩小了,但 0 字节文件仍为 0 字节。
通常,如果我中止操作,文件会突然报告“丢失”的字节,并突然增长到“正确”的大小。
另外,如果我使用 Total Commander 的 Lister 查看文件内部,我可以看到文件中确实有内容。
我想知道,这些字节在哪里?0 字节文件旁边没有类似“.bak”的文件。我的 tmp 目录在另一个驱动器上。根据任务管理器,该程序的内存使用量没有增加Memory (private working set)
。而且,Lister 可以显示内容!
我检查文件系统大小的方法是通过 Total Commander 显示的信息。我定期刷新目录列表,这也会更新可用空间计数器。我也用 进行了测试dir
,得到了相同的结果。我使用的是 NTFS。
为什么报告的大小没有更新?其中的数据存储在哪里?
答案1
这确实是设计使然。这是 Microsoft/IBM 操作系统的长期行为,在 32 位 OS/2、16 位 OS/2、PC-DOS、MS-DOS 以及 DR-DOS(又名 Novell DOS 又名 OpenDOS)中也可以发现。
其行为是仅更新文件的目录条目,在目录列表中显示正确的最后修改时间戳、大小和分配信息,当文件的系统文件表条目关闭时。当引用 SFT 条目的所有进程中的所有打开句柄都关闭时,该条目即被关闭。正在下载“数百 MiB”的程序尚未关闭其打开的文件句柄。因此,它们的 SFT 条目未关闭。因此,目录条目未更新。
如果使用 创建了文件的一个或多个附加句柄— “关闭文件”。 OS/2 Warp:控制程序编程指南和参考.IBM 公司。DosDupHandle
,则文件的内部缓冲区不会写入磁盘,并且其目录条目未更新,直到DosClose
所有句柄都被调用。
在单任务 MS/PC/DR-DOS 上,这有点难以观察到,尽管可以在 DesqView、Windows 3.x 的 386 增强模式下观察到它,TASKMAX
等等。在多任务 OS/2 上,可以定期轻松地观察到它。
这种行为不太受欢迎的特点之一是(在 FAT 卷上),在文件句柄关闭之前进行异常关机将导致CHKDSK
看到零大小的文件并截断迄今为止下载的所有内容。
这种行为与 UNIX 截然不同,在 UNIX 中,可以看到文件随其增长而增长,ls -l
因为该ls
命令查看的是文件元数据的 i 节点(内存中且是最新的),而不是目录项(在本机 UNIX 文件系统上)不具有大小和日期信息)。DOS、OS/2 和 Windows NT 也在增大文件。数据块被分配给文件,SFT 条目跟踪文件大小,f 节点(在 HPFS 卷上)等结构保持最新,并且正如您所观察到的,卷的可用空间正在减少。使用dir
或任何其他报告文件内容的东西,根本无法看到目录项,文件不断增大。
Windows NT 和 NTFS 的情况与 OS/2 和 DOS 的情况非常相似。(命名法已经改变,这对新手来说有点令人困惑。DOS 和 OS/2 有系统文件表项由各个进程中的文件句柄引用,Windows NT 有文件对象由各个进程中的句柄引用。)具有讽刺意味的是,它更类似于自 Windows NT 6.0 以来的旧 IBM/Microsoft 操作系统的行为,其中行为已发生变化。“新”NTFS 行为是,文件的目录条目存储在每个具有文件链接的目录中,并在从 Win32(使用FindFirstFile
/ FindNextFile
)列出目录时使用,仅在文件的文件对象关闭时更新,而附加到文件本身的元数据在目录列表中不可见(并且只能通过 等调用访问GetFileInformationByHandle
),在文件更新时更新。
为了降低非正常关机风险并看到文件大小随着文件写入而增加,有人采用了一个简单的技巧。您也可以使用它。
每当关闭文件的系统文件表条目时,文件的目录条目就会更新,并且当引用它的任何进程中的最后一个打开的文件句柄关闭时,SFT 条目也会关闭。 这不一定是写入文件的进程打开或使用的句柄。 可以使用任何方法来打开文件(创建单独的 SFT 条目)并再次关闭它。它可以是像type
在另一个控制台/会话中运行的命令一样简单:
输入文件> nul
讽刺的是,Total Commander 的 Lister 每次在文件写入时查看文件内容时都会发生这种情况。如果你发现了这一点,你就会问为什么 Total Commander 的 Lister 会神奇地“修复”你的目录列表。答案是 Total Commander 没有什么特别之处。 任何事物单独打开和关闭文件会产生同样的效果。
为了完整起见,请注意,下载程序可以使用系统调用来更新目录条目。DosBufReset
例如,OS/2 就有。然而,这些系统调用还刷新磁盘缓存,但这并不是我们想要的。再次打开文件然后关闭该文件句柄不会刷新磁盘缓存。