如何复现未刷新内核缓冲区导致的数据丢失问题?
如何复现Linux下USB写入未刷入硬件的问题
复现前的准备
- 准备一个普通USB驱动器,挂载到比如
/mnt/usb,先用mount命令确认挂载参数包含async(默认就是该参数,若显示sync则需重新挂载,否则无法复现) - 生成一个100MB的测试文件:执行
dd if=/dev/urandom of=test_data bs=1M count=100,小文件会被内核快速刷入硬件,难以复现缓冲区滞留问题
复现步骤
- 编写模拟导出的测试脚本(替代你的Excel导出逻辑),示例用Python实现:
import os def fake_export(): usb_file = "/mnt/usb/test_excel.xlsx" # 写入大体积内容,让内核缓冲区积压待写入数据 with open(usb_file, "wb") as f: f.write(os.urandom(1024*1024*100)) # 仅刷新用户空间缓冲区,不同步内核到硬件 f.flush() print("导出成功(仅刷用户缓冲区)") if __name__ == "__main__": fake_export()
- 运行脚本,看到
导出成功(仅刷用户缓冲区)的瞬间立刻物理拔除USB驱动器(不要用umount或udisksctl等命令卸载,这类命令会自动触发内核同步,无法复现问题) - 重新挂载USB驱动器,查看
/mnt/usb/test_excel.xlsx:要么文件不存在,要么文件大小远小于100MB,或内容损坏,即复现目标问题
为什么之前的方法未成功?
- 使用命令断开USB:卸载类命令会强制内核将缓冲区数据刷入硬件,相当于自动执行了
fsync,无法复现缓冲区滞留场景 - 手动拔取时机错误:小文件写入后内核会快速自动刷盘(内核有定期同步机制),只有大文件才能让缓冲区积压足够数据,提升复现概率
- 挂载时使用
sync选项:该选项会让所有写入直接同步到硬件,无缓存环节,自然不会出现缓冲区滞留问题
验证fsync的修复效果
修改测试脚本,添加os.fsync强制同步内核缓冲区到硬件:
import os def fake_export_with_fsync(): usb_file = "/mnt/usb/test_excel.xlsx" with open(usb_file, "wb") as f: f.write(os.urandom(1024*1024*100)) f.flush() os.fsync(f.fileno()) # 强制将内核缓冲区数据刷入硬件 print("导出成功(已刷入硬件)") if __name__ == "__main__": fake_export_with_fsync()
运行后立刻拔取USB,重新挂载后文件应完整存在,说明fsync生效
内容的提问来源于stack exchange,提问作者mq白
相关产品推荐
相关产品推荐

