14

如果我使用普通的 IO API 读取和写入单个文件,则可以保证写入在每个块的基础上都是原子的。也就是说,如果我的写入只修改了一个块,操作系统保证要么写入整个块,要么什么都不写入。

如何在内存映射文件上实现相同的效果?

内存映射文件只是字节数组,因此如果我修改字节数组,操作系统无法知道我何时认为写入“完成”,因此它可能(即使不太可能)换出内存中的内存在我的块写入操作的中间,实际上我写了半个块。

我需要某种“进入/离开临界区”,或者在写入文件时将文件的页面“固定”到内存中的某种方法。存在这样的东西吗?如果是这样,是否可以跨常见的 POSIX 系统和 Windows 移植?

4

2 回答 2

5

记日记的技巧似乎是唯一的方法。我不知道这如何与多个应用程序写入同一个文件一起工作。Cassandra 项目有一篇关于如何通过期刊获得绩效的好文章。关键是要确保,日志只记录积极的行动(我的第一种方法是将每次写入的原图像写入日志,允许您回滚,但它变得过于复杂)。

所以基本上你的内存映射文件transactionId在标题中有一个,如果你的标题适合一个块,你知道它不会被破坏,尽管很多人似乎用校验和写了两次:[header[cksum]] [header[cksum]]. 如果第一个校验和失败,请使用第二个。

日志看起来像这样:

[beginTxn[txnid]] [offset, length, data...] [commitTxn[txnid]]

您只需继续附加日记记录,直到它变得太大,然后在某个时候将其翻转。当你启动你的程序时,你检查文件的事务 id 是否是日志的最后一个事务 id——如果不是,你回放日志中的所有事务以同步。

于 2010-10-25T16:41:08.987 回答
0

如果我使用普通的 IO API 读取和写入单个文件,则可以保证写入在每个块的基础上都是原子的。也就是说,如果我的写入只修改了一个块,操作系统保证要么写入整个块,要么什么都不写入。

在一般情况下,操作系统保证使用“普通 IO API”完成的“块写入”是原子的:

  • 块更多是文件系统的概念——文件系统的块大小实际上可能映射到多个磁盘扇区......
  • 假设你的意思是扇区,你怎么知道你的写入只映射到一个扇区?当 I/O 通过文件系统的间接寻址时,没有什么可以说它与扇区的 I/O 很好地对齐
  • 没有什么说你的磁盘必须实现扇区原子性。“真实磁盘”通常可以,但它不是强制性的或保证的属性。遗憾的是,您的程序无法“检查”此属性,除非它是 NVMe 磁盘并且您可以访问原始设备,或者您正在向原始设备发送具有原子性保证的原始命令。

此外,您通常关心多个扇区的持久性(例如,如果发生断电,我在该扇区之前发送的数据是否肯定在稳定存储上?)。如果正在进行任何缓冲,您的写入可能仍然只在 RAM/磁盘缓存中,除非您使用另一个命令首先检查/打开带有请求缓存绕过的标志的文件/设备,并且说这些标志实际上是被尊重的。

于 2020-06-09T03:56:05.190 回答