2

背景是开发 DBMS 内核,特别是数据库检查点处理。游戏规则是,我们需要等待文件上未完成的异步 IO 完成,然后再发出 fsync()。

我们部署的当前解决方案是手动计算运行中的异步 IO,等待此计数变为 0,然后再进行 fsyncing 或 FlushFileBuffer-ing。问题是我们是否真的必须这样做,也许内核/文件系统自己会这样做?

有问题的操作系统主要是 Windows 和 Linux,尽管我也很好奇基于 BSD 的操作系统如何处理这些问题。

在 Linux 上,我们使用 libaio 进行异步 IO。

4

1 回答 1

4

在 Windows 上:是的,对于给定的HANDLE实例,当前的异步 i/o 队列在FlushFileBuffers()执行之前被耗尽。如果你正在编写一个数据库,你真的应该使用NtFlushBuffersFileEx()它,它提供了更精细的同步粒度,产生了巨大的差异。

在 FreeBSD 上:当然是 ZFS,是的。我不能说我已经测试过 UFS,但如果它不一样,我会感到惊讶。在任何情况下,FreeBSD 都将缓存的异步 i/o 实现为内核线程池,只有未缓存的异步 i/o 才是真正的异步。

在 Mac OS 上:不知道,更糟糕的是,磁盘 i/o 语义在最近几个版本中无处不在。它曾经非常好,就像 BSD,但最近它已经走下坡路了。在任何情况下,异步文件 i/o 在 Mac OS 上总是几乎无法使用,最大 16 深度队列限制加上使用信号完成异步 i/o 完成的要求很难与线程代码很好地混合。

在 Linux 上:对于同步 i/o,fsync()如果您的文件系统保证(所有流行的都这样做),yes 会强制执行每个 inode 的总排序。对于在任何情况下都只适用于 i/o 的 libaio,O_DIRECT我相信块存储层会在告诉设备设置屏障之前刷新所有排队的 i/o,除非您禁用了屏障。对于 io_uring(你应该使用它而不是 libaio),对于非O_DIRECTi/o,一旦 io_uring 处理了提交,文件系统对 per-inode i/o 强制执行的顺序是什么。对于带有 i/o 的 io_uring ,块存储层是一个单例,一旦 io_uring 处理了提交O_DIRECT,就应该在整个系统中强制执行排序。

我一直提到“一旦 io_uring 处理了提交”,因为 io_uring 与环形缓冲队列一起工作。如果您将条目添加到提交队列,它将按提交顺序由 io_uring 处理(即队列被排空)。从提交的那一刻到 io_uring 消费提交的那一刻,没有排序。但是一旦 io_uring 消费了提交,目标文件系统就被告知了 i/o,并且无论它实现什么排序保证,它都将应用于它发送回 io_uring 的完成的排序。因此,当使用 io_uring 时,不要在 i/o 提交后继续操作,直到 io_uring 从提交队列中清空您的 i/o 提交请求。这自然会发生,使用系统调用告诉 io_uring 排空队列,或者对于轮询排空,您可以观看“

资料来源:我是WG21 C++ 低级 i/o 标准化参考库的作者。警告:以上所有内容纯粹来自我的记忆和经验,可能会有点烂或错误。

于 2020-06-05T17:23:24.587 回答