持久化内存编程——使用pmemobj库访问持久化内存

前面提到了持久化内存编程的启程资料:

https://blog.csdn.net/SweeNeil/article/details/90293313

在持久化内存开发工具中,提供了各种各样的库,这些在pmem的github介绍以及pmem.io中都有提到,下面就直接把github上面的直接拿出来:

持久化内存编程——使用pmemobj库访问持久化内存_第1张图片

上面描述了各个库的说明,在其中,看到libpmemobj提供的功能还算全面,所以就使用libpmemobj来进行持久化内存的开发。

下面就翻译一篇资料里的内容来实际操作一下持久化内存。

在操作持久化内存之前,如果我们没有持久化内存,那么就需要在内存DRAM中模拟持久化内存Persistent Memory,模拟持久化内存的方式可以参考:https://blog.csdn.net/SweeNeil/article/details/90265226

下面文章翻译自:http://pmem.io/2015/06/13/accessing-pmem.html

在正是开始之前,读者也可以先阅读一些介绍:

http://pmem.io/2014/09/01/nvm-library-overview.html

http://pmem.io/2015/06/12/pmem-model.html

内存池

我们可以阅读overview,从中了解到操作系统以内存映射文件的方式使用持久性内存,我们将其(文件)称为池。

pmemobj库提供了一个易于管理这些池的界面,因此开发者无需手动创建文件或对其进行mmap。

创建池使用pmemobj_create API函数完成的,该函数采用了创建文件的函数和布局所需的常用参数,布局是开发者设定的用于标识池的字符串。

创建完成后,要使用就需要用pmemobj_open API,传递给pmemobj_open的布局必须与创建池的布局相匹配。

与任何其他OS资源一样,当不再需要持久性内存池时,通常在应用程序结束时,必须使用pmemobj_close释放池。为了验证池的完整性,在libpmemobj也提供了pmemobj_check函数来验证所有必需的元数据是否一致。

持久性指针

现在我们已经映射了内存区域,如何访问它?

我们可以考虑一下常规指针,将它归结为一种非常基础的方式,指针是虚拟地址空间的开始到它所指向的事物的开头之间的字节数。

现在将指针转换到持久性内存,在一个应用程序中可以打开多个池,持久性指针的大小是常规指针的两倍,并包含从池的开头(而不是VAS)和池的唯一ID的偏移量。

结构本身看起来像这样:

typedef struct pmemoid {
	uint64_t pool_uuid_lo;
	uint64_t off;
} PMEMoid;

如果知道了池映射到的虚拟地址,则可以执行简单的添加以获取直接指针,如下所示:

(void *)((uint64_t)pool + oid.off)

这正是pmemobj_direct的作用,它采用PMEMoid(持久性指针)并将其转换为可以解除引用的常规指针。

池ID用于确定池当前映射的位置(因为每次启动应用程序时,内存映射区域的实际地址可能不同)。

所有打开的池都存储在具有2个散列函数的cuckoo散列表(cuckoo hash table)中,因此这意味着当调用pmemobj_direct时,最多会发生两次查表就可以找到池地址。

root对象

考虑以下场景:

 - 分配一块持久化内存(假设一个类似malloc的接口)

- 在持久化内存中写一个字符串

- 关闭应用程序

这时候如何找到包含刚才创建的字符串的指针?

想要的数据将在池中的某个位置,但除了扫描整个文件以找到匹配的字符之外,无法真正找到它。

例如,可以在池中选择一个随机偏移量并将其视为已知地址。但这是错误的,就像在虚拟地址空间中随机写错一样 - 它很可能会无意中覆盖某些东西。

那我我们该怎么做呢,在内存池中始终可以查找的已知位置是root对象。它是所有内存结构都可以附加的锚点,在真正需要的只是一个而不是动态变化的情况下,数据结构只能依赖于root对象。

pmemobj_root函数中的大小是开发者想要作为root对象的结构的大小,因此通常开发者需要编写如下内容:

PMEMoid root = pmemobj_root(pop, sizeof (struct my_root));

root对象最初为0,因此无需担心初始化。

如果要调整对象的大小,只需将不同的大小传递给函数即可自由地执行此操作 - 因此,当向结构中添加新变量时,也无需担心,新区域也将初始归零。

root对象是从池中分配的,当不可能进行就地重新分配时,将使用不同的偏移量创建新对象,因此不要将root持久性指针存储在任何没有经过考虑的位置。

安全存储数据

前面讲解的例子都是存储数据的位置,接下来将介绍如何存储数据,有下面的一段代码:

void set_name(const char *my_name) {
    memcpy(root->name, my_name, strlen(my_name));
}

如果root变量是易变的,那么这将是一个完全有效的代码,但如果它是持久的,则此函数的结果不是确定性的。

内存可以在应用程序崩溃中幸存,在创建写入持久性内存的程序时,我们必须格外小心,以确保应用程序始终处于我们可以识别和使用的状态,无论是否准确的时刻被中断 - 不要假设应用程序始终优雅退出,这可能是99%的情况,但是当出现意外情况时,可能会遇到不可恢复的应用程序状态并丢失所有数据。

回到代码,假设可以识别root-> name变量的归零状态,所以如果应用程序在memcpy启动之前崩溃,那么一切都很好。当应用程序在复制过程中崩溃时会发生什么?好吧,如果你的名字是Brianna,实际存储的值可能是Brian  - 这是完全有效的,但不是我们想要的。

当复制完成后,程序可以中断而不会出现问题吗?嗯......不。还必须考虑CPU缓存及其刷新顺序。在这种情况下,如果Bri部分恰好位于未及时刷新的不同高速缓存行上,则名称可能会成为动画。那么,如何解决这一切呢?

void set_name(const char *my_name) {
    root->length = strlen(my_name);
    pmemobj_persist(&root->length, sizeof (root->length));
    pmemobj_memcpy_persist(root->name, my_name, root->length);
}

注意,上述代码在复制之前存储缓冲区的长度,因此在读取时可以仔细检查名称是否正确。

_persist后缀函数确保它们操作的内存范围从CPU刷新并安全地存储在介质上。

因此,在第4行,100%确定root-> length包含我们想要的内容。

pmemobj库有更方便的方法,比如事务,但了解基础知识有助于理解更高级的技术。但是不用担心 - 在后面的系列中以至少两种不同的方式编写完全相同的例子。

基本原则是,在当前的硬件架构中,只能以原子方式写入8个字节的内存,所以这样的事情是正确的:

root->u64var = 123;
pmemobj_persist(&root->u64var, 8);

但是下面的是错误的:

root->u64var = 123;
root->u32var = 321;
pmemobj_persist(&root->u64var, 12);

上面就是持久化内存的编程要点。

应用案例

通过前面的介绍已经了解了一些有价值的知识,现在可以来使用它。

应用案例将编写2个应用程序,一个将字符串写入内存,另一个读取完全相同的字符串 - 但前提是它已正确写入。

两个应用程序都需要下面的头文件:

#include 
#include 
#include 
#include "layout.h"

作为一般规则,使用libpmemobj时不需要libpmem,libpmemobj提供了所有必需的功能。 layout.h文件包含两个.c文件都需要的声明:

#define LAYOUT_NAME "intro_0" /* will use this in create and open */
#define MAX_BUF_LEN 10 /* maximum length of our buffer */

struct my_root {
	size_t len; /* = strlen(buf) */
	char buf[MAX_BUF_LEN];
};

首先,创建writer.c文件来做第一部分工作,也就是将字符串写入内存:

在这里使用第一个参数的名称创建池文件。

不要忘记在pmemobj_create中使用正确的文件模式,否则将无法打开或修改池。

int main(int argc, char *argv[])
{
	PMEMobjpool *pop = pmemobj_create(argv[1], LAYOUT_NAME, PMEMOBJ_MIN_POOL, 0666);
	if (pop == NULL) {
		perror("pmemobj_create");
		return 1;
	}

	...

	pmemobj_close(pop);
	return 0;
}

接下来,我们请求根对象并将其转换为可用的直接指针。

因为这是在创建池之后完成的,所以我们可以确保root指向的struct my_root是零。

PMEMoid root = pmemobj_root(pop, sizeof (struct my_root));
struct my_root *rootp = pmemobj_direct(root);

我们最大读取9byte进入到临时缓冲区buf中:

char buf[MAX_BUF_LEN];
scanf("%9s", buf);

我们将此缓冲区写入持久性:

rootp->len = strlen(buf);
pmemobj_persist(pop, &rootp->len, sizeof (rootp->len));
pmemobj_memcpy_persist(pop, rootp->buf, my_buf, rootp->len);

到目前为止writer.c编程完毕,在文末会贴案例完整代码。

下面是reader.c的主函数:

int main(int argc, char *argv[])
{
	PMEMobjpool *pop = pmemobj_open(argv[1], LAYOUT_NAME);
	if (pop == NULL) {
		perror("pmemobj_open");
		return 1;
	}

	PMEMoid root = pmemobj_root(pop, sizeof (struct my_root));
	struct my_root *rootp = pmemobj_direct(root);

	...

	pmemobj_close(pop);

	return 0;
}

这次当我们打开池时,root对象不会被归零 - 它将包含writer负责存储的任何字符串。 所以,阅读之前writer写的字符串:

if (rootp->len == strlen(rootp->buf))
	printf("%s\n", rootp->buf);

现在可以编译这两个应用程序,运行并验证它们是否执行了所讲述的内容。

完整案例代码(our repository):

layout.h:

/*
 * Copyright 2015-2017, Intel Corporation
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in
 *       the documentation and/or other materials provided with the
 *       distribution.
 *
 *     * Neither the name of the copyright holder nor the names of its
 *       contributors may be used to endorse or promote products derived
 *       from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/*
 * layout.h -- example from introduction part 1
 */


#define LAYOUT_NAME "intro_1"
#define MAX_BUF_LEN 10

struct my_root {
	size_t len;
	char buf[MAX_BUF_LEN];
};

writer.c:

/*
 * Copyright 2015-2017, Intel Corporation
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in
 *       the documentation and/or other materials provided with the
 *       distribution.
 *
 *     * Neither the name of the copyright holder nor the names of its
 *       contributors may be used to endorse or promote products derived
 *       from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/*
 * writer.c -- example from introduction part 1
 */

#include 
#include 
#include 

#include "layout.h"

int
main(int argc, char *argv[])
{
	if (argc != 2) {
		printf("usage: %s file-name\n", argv[0]);
		return 1;
	}

	PMEMobjpool *pop = pmemobj_create(argv[1], LAYOUT_NAME,
				PMEMOBJ_MIN_POOL, 0666);

	if (pop == NULL) {
		perror("pmemobj_create");
		return 1;
	}

	PMEMoid root = pmemobj_root(pop, sizeof(struct my_root));
	struct my_root *rootp = pmemobj_direct(root);

	char buf[MAX_BUF_LEN] = {0};
	if (scanf("%9s", buf) == EOF) {
		fprintf(stderr, "EOF\n");
		return 1;
	}

	rootp->len = strlen(buf);
	pmemobj_persist(pop, &rootp->len, sizeof(rootp->len));

	pmemobj_memcpy_persist(pop, rootp->buf, buf, rootp->len);

	pmemobj_close(pop);

	return 0;
}

reader.c:

/*
 * Copyright 2015-2017, Intel Corporation
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in
 *       the documentation and/or other materials provided with the
 *       distribution.
 *
 *     * Neither the name of the copyright holder nor the names of its
 *       contributors may be used to endorse or promote products derived
 *       from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/*
 * reader.c -- example from introduction part 1
 */

#include 
#include 
#include 

#include "layout.h"

int
main(int argc, char *argv[])
{
	if (argc != 2) {
		printf("usage: %s file-name\n", argv[0]);
		return 1;
	}

	PMEMobjpool *pop = pmemobj_open(argv[1], LAYOUT_NAME);
	if (pop == NULL) {
		perror("pmemobj_open");
		return 1;
	}

	PMEMoid root = pmemobj_root(pop, sizeof(struct my_root));
	struct my_root *rootp = pmemobj_direct(root);

	if (rootp->len == strlen(rootp->buf))
		printf("%s\n", rootp->buf);

	pmemobj_close(pop);

	return 0;
}

编译:

gcc writer.c -o writer -lpmemobj -lpmem -pthread
gcc reader.c -o reader -lpmemobj -lpmem -pthread

运行结果展示:

writer:

reader:

上面的那个参数就是我们模拟挂载的持久化内存目录~

你可能感兴趣的:(存储,Linux,持久化(性)内存/非易失性内存)