我发现小容量 FAT(FAT12)格式的 USB 闪存驱动器上的 FAT 写入延迟,即使驱动器的策略设置为“快速移除”。(我相信这意味着SurpriseRemovalOK
设置了标志)。我捕获了通过 USB 发送到驱动器的 SCSI 命令:文件截断写入立即发生,整个文件(2 个 512 字节扇区长)随后立即写入,但随后在 FAT 更新以反映文件写入之前会有 20-90 秒的延迟。
驱动器的大小非常重要。我已经测试过,发现 15MB 及以下大小的 FAT 文件系统存在问题。在 16MB 及以上大小的文件系统上,写入不会延迟。当我在 Windows 中格式化驱动器时,我看到使用 FAT12 和 FAT16 之间的断点是 16MB。(稍后添加的注释:但 FAT12/FAT16 断点取决于簇数,而不是绝对文件系统大小)。
在 16MB 及以上容量的 U 盘上,Windows 在写入之前会发送 SCSIPrevent/Allow Medium Removal
命令,要求不要移除设备。U 盘实际上会对这些请求返回失败(因为它无法保证不移除),但 Windows 还是会尝试。15MB 及以下容量的跟踪显示不 Prevent/Allow Medium Removal
命令。
(我在使用支持包含 Python 代码的微型 FAT 文件系统的微控制器板时发现了这个问题。当微控制器检测到对文件系统的写入时,它会等待一段时间以完成写入,然后自动重新启动并运行新编写的 Python 代码。但由于写入延迟,微控制器看到的是损坏的代码或损坏的文件系统。)
为什么尽管设置了“快速删除”,但写入 FAT 仍然延迟了这么长时间?我可以通过对驱动器执行“弹出”来强制写入,但这违背了“快速移除”的承诺。如果我提前拔出驱动器,它将有一个错误的 FAT 表。这与下面屏幕截图中关于不必使用“安全移除硬件”的声明相矛盾。这是一个错误还是我遗漏了什么?有没有办法强制立即进行所有写入,而无需手动“弹出”?
以下是从 Wireshark/USBPcap 跟踪中截取的一段显示该问题的摘录。我截断了一个现有文件,然后写入了它的新副本。我添加了注释###
。大多数对 USB 驱动器的写入发生在跟踪开始后的 5 秒左右,但最后的 FAT 写入要到 26 秒才会发生。
No. Time Source Destination Protocol Length Info
### write directory entry to truncate file
13 5.225586 host 1.2.2 USBMS 58 SCSI: Write(10) LUN: 0x00 (LBA: 0x00000041, Len: 8)
14 5.225838 host 1.2.2 USB 4123 URB_BULK out
### write FAT entries to truncate file
16 5.230488 host 1.2.2 USBMS 58 SCSI: Write(10) LUN: 0x00 (LBA: 0x0000003b, Len: 1)
17 5.230707 host 1.2.2 USB 539 URB_BULK out
19 5.235110 host 1.2.2 USBMS 58 SCSI: Write(10) LUN: 0x00 (LBA: 0x0000003e, Len: 1)
20 5.235329 host 1.2.2 USB 539 URB_BULK out
### write directory entry for
22 5.252672 host 1.2.2 USBMS 58 SCSI: Write(10) LUN: 0x00 (LBA: 0x00000041, Len: 8)
23 5.252825 host 1.2.2 USB 4123 URB_BULK out
### write out file data (2 sectors of 512 bytes)
25 5.257416 host 1.2.2 USBMS 58 SCSI: Write(10) LUN: 0x00 (LBA: 0x000000c1, Len: 2)
26 5.257572 host 1.2.2 USB 1051 URB_BULK out
### 20 second delay
### finally, write FAT entries to indicate used sectors
79 26.559964 host 1.2.2 USBMS 58 SCSI: Write(10) LUN: 0x00 (LBA: 0x0000003b, Len: 1)
80 26.560191 host 1.2.2 USB 539 URB_BULK out
82 26.560834 host 1.2.2 USBMS 58 SCSI: Write(10) LUN: 0x00 (LBA: 0x0000003e, Len: 1)
83 26.560936 host 1.2.2 USB 539 URB_BULK out
我使用常规闪存驱动器以及模拟微型 USB MSC 驱动器的微控制器板在 Windows 7 和 Windows 10 上生成了这样的跟踪。
需要明确的是,这是一个 FAT12 格式的驱动器,在 Windows 格式化工具中仅称为“FAT”。
答案1
我可能已经找到了导致该问题的实际 Windows 驱动程序代码。
MS 恰好将 FAT 文件系统驱动程序包含在示例驱动程序代码包中。该驱动程序中有几处地方,如果文件系统是 FAT12,驱动程序将不会费心执行诸如设置脏位(FAT12 可能没有)或刷新 FAT 数据之类的操作。
https://github.com/Microsoft/Windows-driver-samples/blob/master/filesys/fastfat/verfysup.c#L774 https://github.com/Microsoft/Windows-driver-samples/blob/master/filesys/fastfat/cachesup.c#L1212 也许最关键的是: https://github.com/Microsoft/Windows-driver-samples/blob/master/filesys/fastfat/cleanup.c#L1101
在最后一个链接中,cleanup.c
如果文件系统是 FAT12,则不会刷新 FAT。我认为这可能导致我看到的行为:
//
// If that worked ok, then see if we should flush the FAT as well.
//
if (NT_SUCCESS(Status) && Fcb && !FatIsFat12( Vcb) &&
FlagOn( Fcb->FcbState, FCB_STATE_FLUSH_FAT)) {
Status = FatFlushFat( IrpContext, Vcb);
在 Windows 反馈中心向 Microsoft 报告https://aka.ms/btvdog(在反馈中心打开的特殊 URL)。