知乎日报

每日提供高质量新闻资讯

头图

为什么 Steam 删除 80G 的游戏只用一秒?

RitaE / CC0

木头龙,头像换了

这个问题下截止到目前的回答还没看到一个特别准确的。

既然是 80G 的 Steam 游戏,默认操作系统是 Windows,文件系统是 NTFS。Windows 支持的其它文件系统,FAT/FAT32 分区最大只有 8G/32G(理论上 FAT32 分区大小可以是 2T,但 Windows 不允许用 FAT32 格式化大于 32G 的分区),放不下 80G 的游戏,exFAT 默认只能用来格式化移动存储设备不能用来格式化固定磁盘,所以这个问题下用 FAT 来解释的回答大概率都是错的。ReFS 估计没几个人在用吧?

在 NTFS 分区里面,一个文件,根据文件大小的不同,有 3 处或者 5 处相关的信息:

  1. 在$MFT 里面的 FILE 记录,大小固定为 1K。$MFT(Master File Table,主文件表)是 NTFS 最重要的文件,默认隐藏不能访问,记录着这个分区内所有的目录和文件信息。一个文件在$MFT 里面的记录主要记录了文件名、所在文件夹的记录号、大小、创建 / 上次修改 / 上次修改$MFT 记录 / 上次访问时间等属性。如果这条 1K 记录剩余的空间大于文件大小,使用一个特殊的属性直接在记录中保存文件的数据,这个属性在 NTFS 称为常驻属性;如果剩余空间小于文件大小,文件数据需要使用额外的簇保存,使用另一种属性记录这个保存这个文件数据所占用的簇,这个属性称为非常驻属性。
  2. 在上级文件夹的索引数据中的记录,大小不定,通常在 88~600 字节之间。每个文件夹(包括根目录)在内,会记录本文件夹下所有子文件夹、文件的信息,包括文件在$MFT 里面的记录号、大小、创建 / 上次修改 / 上次修改$MFT 记录 / 上次访问时间等属性,这些信息占用 82 字节。此外还有这个文件 / 子文件夹的文件名,最大 255 个字符,每个字符占使用 unicode 编码,占两个字节。最后会补齐 8 个字节。此外,为了兼容 DOS 的 8.3 文件名,每个文件 / 子文件夹通常会有另一条文件名是 8.3 格式的记录,这条记录的大小一般不会大于 96 字节。
  3. 在$Secure 里面的记录,数量和大小不定。$Secure 记录每个文件的权限,例如某个用户 / 组对这个文件具有什么样的权限。对于家用情况,没多少用户 / 组,一个文件的权限数据通常很小不会超过 4K。
  4. 在$Bitmap 中对应的位。NTFS 分区中每一簇按顺序对应$Bitmap 文件中的 1bit,0 表示这一簇未被占用,1 表示被占用。如果是文件在$MFT 记录中的数据属性为非常驻属性,文件所占用的每一簇在$Bitmap 文件中对应的 bit 置 1。
  5. 文件数据实际占用的簇。一般来说稍微大一点的文件,都很可能会形成多个碎片,但这个问题是删除文件,无需考虑碎片的情况,这个后面会提到。

根据第 1 项的说明,如果文件夹数据 / 文件大小很小的话,上面 4、5 两项是没有的。如果是簇大小为 4K 的 NTFS 分区,80G 的文件在$bitmap 中占用 80×1024×1024÷4=20Mbit=2.5MB。如果考虑到这些簇可能散乱分布在整个分区中的不同位置,需要把整个$Bitmap 文件更新一遍的话,一个 1TB 的分区的$Bitmap 文件是 32MB。

所以,每删除一个文件 / 文件夹,都要更新 1~4 项的信息。第 5 项无需更新,文件所使用的簇在$Bitmap 中对应的位置 0 后,将来如果这些簇用来保存新的文件数据会直接覆盖。当然,如果是使用固态硬盘的话,Windows 会发送 trim 指令给固态的主控,主控会在空闲的时候根据磨损算法回收对应的 Page。

如果是机械硬盘的话,在没有缓存的前提下,单是找到 1~3 项的记录通常就需要十多次寻道。每一处记录的更新必然有一次寻道,但对于固态来说只要文件数量不是太多,这个寻道(固态其实应该叫寻址更合适点)时间都可以在 1 秒内完成。而每一处信息更新通常只需要写入 1 簇(4K)的数据,就算是机械硬盘,这个数据传输时间也是基本可以忽略的,固态就更不用说了。

最后,根据前面的论述,如果一个游戏有 1024 个文件的话,写入数据量是 1024×12KB=12MB,加上$Bitmap 文件的 2.5~32MB,总计是 14.5MB~44MB。SATA 固态硬盘通常 4K 随机读性能在 20MB/s 以上,4K 写性能 100MB/s 以上,在这些数据都在缓存中的前提下,还是可以在 1 秒内完成的。如果文件数量更少,就更加没有压力了。


某些回答里面猜测的这些索引记录是否会在 Steam 安装游戏的时候使用预分配空间是使得这些记录集中在一起,我可以肯定不是。下图中灰色底色的行是我电脑上的 Steam 里面泰坦之旅这个游戏的文件夹以及文件夹下若干个文件的起始簇编号、扇区地址:

可以看到,这几个文件 / 文件夹不管是$MFT 记录号还是簇编号,都并不连续。另外,这个游戏的文件加上目录数量不超过 600。

Steam 能做的,估计是把这些记录所在的扇区缓存到内存中,减少读取数据的寻道 / 寻址次数。毕竟前面的计算已经足以说明,就算几个游戏加起来如果有一千个文件,这些数据加起来也就 12MB,一万个也就 120MB(不算$Bitmap),对于今天的主流配置来说这点内存占用完全可以忽略。而不管是删除游戏还是我们平时打开游戏来玩,就不需要从硬盘中读取这些数据了。


最后,关于 NTFS 分区上如何找到一个文件的$MFT 记录号、上级文件夹的索引数据,有兴趣的朋友可以看看我前几天写的文章:

木头龙:为什么说固态硬盘的 4K 性能很重要?