Persistent Memory (pmem, PM) 是很特殊的设备,它既是内存接口,又有非易失的存储特性。因此:作为内存,它即面临内存所特有的cache coherence等问题;作为存储,它也面临外存系统所特有的崩溃一致性、持久化等问题。 因此,构建一个基于PM的存储系统也所要考虑的问题是比较多的。近年来,学术界大量文章都在关注PM存储系统或者PM编程模型,比如PM感知文件系统、PM KV存储、PM事务框架等。 本文分为持久化、原子性和崩溃一致性三小节,讨论了PM系统编程的一些必须注意的点。文章整体基于我之前所写调研综述的相关部分,并进行了精简和修改。本文参考了很多文章并加入了自己的理解,若有问题,恳请指正。 1. 持久化 (Durability) 然而,对PM的store操作后并不能保证数据肯定存储到PM上,这是因为CPU和PM之间还有几层cache,要保证持久化,我们需要用flush / fence相关的指令。 当使用PM时,CPU对内存的store操作可能会暂存在硬件管理的cache或者write buffer中,若用户不强制刷cacheline到内存,无法保证store操作的数据何时写到内存中。在原来的情况下,内存为掉电易失的DRAM,所以刷或者不刷cacheline只可能牵扯到系统的性能,而不会影响系统的正确性;但是在使用PM时,由于持久化的存储是我们的目标之一,我们就要额外注重数据持久化的时机,以进行更精确的控制。 在使用块设备时,类似的问题同样存在。写操作一般会被缓存到系统的page cache中,因此需要用户调用fsync等函数来进行磁盘同步,让数据真正持久化地存储到磁盘上。解决PM cache数据写回的问题和块设备同步磁盘问题的思路是类似的,只不过方法不同。如下图,在x86平台中,当数据离开CPU cache进入PM或者进入电容保护的持久区(虚线框),便意味着数据已经被持久化,因此只要使用将数据刷出cache并写回PM的指令,就可以保证相应数据持久化存储到PM了。 由于一般在使用PM时,是通过内存映射的方式进行的,所以使用操作系统实现的msync函数是可行的(msync和fsync具有相同的语义)。除此之外,用户也可以直接调用x86平台的cache刷新指令进行数据同步。 指令 说明 CLFLUSH 几乎所有CPU都支持,但没有任何并发性,串行地执行 CLFLUSHOPT + SFENCE 比CLFLUSH新,但是不是串行执行的,因此需要SFENCE CLWB + SFENCE 相对CLFLUSHOPT,可以在刷入PM后仍然让cache保持有效,局部性较好的数据使用此命令可以提升性能 NT stores + SFENCE 即non-temporal store,直接跳过cache的store命令,因此不需要刷新cache WBINVD 只能在内核模式用,将所有CPU的cache 刷入PM,一般不用,太影响性能 上表列出了其他的cache刷新指令,它们的行为各有不同,需要依据场景进行选择。除表中最后一项外,其他指令都是可以在用户空间直接使用的。在用户空间调用cacheline刷新指令的好处是不用切换到内核态,且用户能更清楚地知道哪块数据需要马上写回PM,所以用户的控制更精细,刷新指令的性能也要好于msync。但是,一些PM感知文件系统也需要msync的控制权,因为数据刷入PM若需要造成PM感知文件系统的metadata改变,那么用户空间使用cacheline刷新指令将导致PM感知文件系统metadata的不一致。所以用户程序应该仅在确保文件系统安全的情况下才使用cacheline刷新指令。 2. 原子性 (Atomic Updating) 这里的原子性指的是原子更新粒度(原子操作)或并发时的原子可见性(隔离性),而非ACID事务的原子性。虽然ACID事务的原子性也是需要借助原子操作实现,这里的原子性更类似与ACID中的I(isolation, 隔离性)。 PM是内存接口,其原子性操作和内存类似,因此其访问原子性也和内存一样,在无锁保护的情况下, x86 平台上支持8、16 或 64 […]