前面提到了持久化内存编程的启程资料:
https://blog.csdn.net/SweeNeil/article/details/90293313
在持久化内存开发工具中,提供了各种各样的库,这些在pmem的github介绍以及pmem.io中都有提到,下面就直接把github上面的直接拿出来:
上面描述了各个库的说明,在其中,看到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时,最多会发生两次查表就可以找到池地址。
考虑以下场景:
- 分配一块持久化内存(假设一个类似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:
上面的那个参数就是我们模拟挂载的持久化内存目录~