来源:原创

如需转载,请注明来自 MemArk 技术社区(​​MemArk 技术社区 - 助力先进存储架构演进​​)

关于作者

杨俊,博士毕业于香港科技大学计算机系,在数据库和存储系统上有超过十年的丰富研究和实践经验;现就职于第四范式任系统架构师,同时也是分级存储技术社区 MemArk (​​https://memark.io/​​​) 的核心成员 。近期,由于其在持久内存编程核心社区 Persistent Memory Programming (​​https://pmem.io/​​)的卓越贡献,成为被该社区吸纳的中国第一位社区核心贡献者(reviewer)。本文将会介绍作者最近一次的代码贡献,对该社区的后续技术发展有深远影响。

一、背景

持久内存(PMem) 虽然可以保证写入PMem的数据掉电重启后不会丢失,但写入PMem的数据往往需要先写到CPU Cache里,再通过一系列CPU 指令把数据刷到PMem中。由于PMem和CPU的硬件限制,向PMem中写入大于8字节的数据并持久化无法保证写操作的原子性(即如果在持久化写入数据过程中掉电,无法保证数据完整写完),所以在PMem中保证持久化​​数据结构​​的数据一致性是非常有挑战性的。如果该持久化数据结构还需要支持正确的多线程写操作,保证数据一致性将变得更加复杂。本文主要描述此背景下的持久化数据的一致性、可见性问题。

本文适合对于持久化编程有一定基础了解的开发者阅读,本文主要包含内容:

  1. 介绍PMem无锁编程中的数据可见性、一致性问题及解决方法。
  2. 介绍本文作者最近刚合入 libpmemobj-cpp 的一个PR,专门为方便实现Single-Writer-Multiple-Reader(SWMR)多线程持久化数据结构的一种自带原子性的持久化指针(Atomic Persistent Pointer)。​​可在此具体参考该 PR 的讨论开发过程。​​

二、PMem的无锁编程

数据可见性是PMem无锁编程的非常重要的难点之一。例如某一线程使用STORE指令修改了一个内存数据,新数据可能在未持久化到PMem时(仍在CPU Cache中)就被另一线程读取。假设有如下两个线程分别使用带来原子性的atomic_write和atomic_read来写入和读取数据:

// 线程1
// pmem->a初始化为0
atomic_store(&pmem->a, 1); // 可见性:是, 持久化:未知
pmem_persist(&pmem->a, sizeof(pmem->a)); // 可见性:是, 持久化:是

// 线程2
// pmem->b初始化为0
if (atomic_load(&pmem->a) == 1) {
pmem->b = 1; // 可见性:是, 持久化:未知
pmem_persist(&pmem->b, sizeof(pmem->b)); // 可见性:是, 持久化:是
}

更多文章请关注《万象专栏》