持久性内存编程——事务性动态内存分配

    xiaoxiao2023-10-23  164

    pmemobj库包含一个从头开始实现的内存分配器,它在设计时考虑了持久性内存。它有两套独立的API:非事务和事务。

    本文讲解持久化内存的事务性的动态内存分配,原文来自:http://pmem.io/2015/06/17/tx-alloc.html

    目录

    事务分配


    事务分配

    看下面一段现在的易失性内存分配:

    struct rectangle { int a; int b; }; int area_calc(const struct rectangle *rect) { return rect->a * rect->b; } ... struct rectangle *rect = malloc(sizeof *rect); if (rect == NULL) return; rect->a = 5; rect->b = 10; int p = area_calc(rect); /* busy work */ free(rect):

    随着科技的发展,现在我们即将可以使用持久性内存,那我们就需要知道如何把上述的代码通过想要的类malloc与free函数修改成适应持久化内存的分配,接下来就来讲解持久性内存的分配布局:

    /* struct rectangle doesn't change */ struct my_root { TOID(struct rectangle) rect; }; POBJ_LAYOUT_BEGIN(rect_calc); POBJ_LAYOUT_ROOT(rect_calc, struct my_root); POBJ_LAYOUT_TOID(rect_calc, struct rectangle); POBJ_LAYOUT_END(rect_calc);

    在编程的过程中,我们需要注意 root 对象和所有其他结构的两个不同的宏。

    同时area_calc函数必须更改为使用持久性指针:

    int area_calc(const TOID(struct rectangle) rect) { return D_RO(rect)->a * D_RO(rect)->b; }

    上述代码中,TOID前面的 const 限定符意味着不允许在此对象上使用D_RW,它不会编译。

    rectangle 对象将在事务中分配和初始化,并且由于这是持久性内存,因此在应用程序重新启动后无法通过某种方式访问它们就无法分配对象 - 为此,我们将使用根对象的rect变量。

    TOID(struct my_root) root = POBJ_ROOT(pop); TX_BEGIN(pop) { TX_ADD(root); /* we are going to operate on the root object */ TOID(struct rectangle) rect = TX_NEW(struct rectangle); D_RW(rect)->x = 5; D_RW(rect)->y = 10; D_RW(root)->rect = rect; } TX_END int p = area_calc(D_RO(root)->rect); /* busy work */

    在上面的代码中只有一个新东西,TX_NEW宏。它只是使用sizeof(T)字节分配内存块并返回TOID(T) - 因此只能将其分配给正确的类型。如果要自己指定对象的大小(对于数组和事物),可以使用TX_ALLOC,对于清零的内存,可以使用Z前缀变体。同样重要的是要注意所有新对象在提交时自动保留 - 不要自己调用pmemobj_persist  - 作为一般规则,开发者不需要在事务中保留任何内存。开发者可能也无法识别TX_ADD,但只是pmemobj_add_range伪装的。在我们使用完这个对象后,我们想要释放它,这里是如何做到的:

    TX_BEGIN(pop) { TX_ADD(root); TX_FREE(D_RW(root)->rect); D_RW(root)->rect = TOID_NULL(struct rectangle); } TX_END

    上述的释放代码,也必须在事务中执行,因为它是一个两步操作。你不太可能想要释放带有旧值的释放指针,这就是你必须记住NULL赋值的原因,它是一个类型化的NULL,有两种选择:

    D_RW(root)->rect.oid = OID_NULL;

    或者:

    TOID_ASSIGN(D_RW(root)->rect, OID_NULL);

    上述两种方式,没有功能差异,因此选择归结为个人偏好。

    事务分配的使用类似于开发者通常编写程序的方式,但增加了跟踪所有更改的开销。

    最新回复(0)