File(s): | Download |
License: | BSD 3-clause License |
Optimized for... | |
---|---|
OS: | Linux* kernel version 4.3 or higher |
Hardware: | Intel® Optane™ DC persistent memory and 2nd Gen Intel® Xeon® Scalable processor Emulated: See How to Emulate Persistent Memory Using Dynamic Random-access Memory (DRAM) |
Software: (Programming Language, tool, IDE, Framework) |
C Compiler, Persistent Memory Development Kit (PMDK) libraries |
Prerequisites: | Familiarity with C |
Introduction
In this article and accompanying code sample, we show how to create a “Hello World” program using the Persistent Memory Development Kit (PMDK) libpmemobj library. libpmemobj is a C language library that provides an interface designed to make it easy to allocate and access objects in persistent memory files, which are called pools. We demonstrate how to use libpmemobj API functions to create, open, close, and manage data in a pool. The pool can reside on any media (such as a regular hard disk drive or a solid-state drive), but the best performance is obtained when it is located in byte-addressable persistent memory. In this example, we use Intel® Optane™ DC persistent memory modules, but if you can’t get your hands on those or another persistent memory product, you can emulate a persistent memory region using dynamic random-access memory (DRAM).
Prerequisites
The article assumes that you have a basic understanding of persistent memory concepts and are familiar with features of PMDK. If not, visit Persistent Memory site on Intel® Developer Zone, where you will find the information you need to get started.
Code Sample Design
This code sample creates a persistent memory pool where it stores a “Hello…” message. Depending on user input, the program performs a read from or a write to the pool. In the write_hello_string() function, the program creates a pool and writes the “Hello…” message to it. In the read_hello_string() function, the program reads the message back from the pool.
Code Walk-through
In the main program, we define an input array containing the text “Hello Persistent Memory!!!”. The program will pass the buf variable into the write_hello_string() function for writing into persistent memory.
Next, the program parses the options passed to it, either -r or -w, and decides whether to read from or write to persistent memory.
The write_hello_string Function
For option -w, the program calls the write_hello_string function. This function completes the write to persistent memory in the five steps shown in the following code snippet:
Step 1 – Create a pool
Use pmemobj_create() to create a persistent memory pool.
PMEMobjpool *pmemobj_create(const char *path, const char *layout,
size_t poolsize, mode_t mode);
const char *path
- Location of memory pool fileconst char *layout
- Name for the object layout in the poolsize_t poolsize
- Minimum pool size allowed by the library is 6 MB. We pass the predefined macro “PMEMOBJ_MIN_POOL” here.mode_t mode
– Memory pool file permissions
Step 2 – Get Root Object
Once the pool has been created, get a pool entry point. Call pmemobj_root(), which provides an entry point for all other persistent objects allocated using the libpmemobj API. There is exactly one root object in each pool, and we recommend that you use only one pool in your application.
pmemobj_root() has the following syntax:
PMEMoid pmemobj_root(PMEMobjpool *pop, size_t size);
PMEMobjpool *pop
- Handle to persistent memory poolsize_t size
- Size of root object
Step 3 – Write the “Hello…” message to persistent memory
Now you have an entry point to the pool. Get a handle to its memory location by using pmemobj_direct(). It returns a pointer to a PMEMoid object with a handle Object Identifier (OID ).
It has the following syntax:
void *pmemobj_direct(PMEMoid oid);
PMEMoid OID
- An object handle of type PMEMoid
After getting the root handle, make the pool persistent using the pmemobj_persist() function. This function forces any changes in the range [addr, addr+len] to be stored durably in persistent memory. There are no alignment restrictions on this range, but it may be expanded as necessary to meet platform alignment requirements. pmemobj_persist() has the following syntax:
void pmemobj_persist (PMEMobjpool *pop, const void *addr, size_t len);
PMEMobjpool *pop
- Handle to persistent memory pool popconst void *addr
- Address location of persistent memorysize_t len
- Size of persistent memory to persist
You can now copy the “Hello…” string to the pool by using the pmemobj_memcpy_persist() function, which combines a regular memcpy with a pmemobj_persist () call. It has the following syntax:
void *pmemobj_memcpy_persist(PMEMobjpool *pop, void *dest,
const void *src, size_t len);
PMEMobjpool *pop
- Handle to persistent memory pool popvoid *dest
- Destination address location of persistent memoryconst void *src
- Source address location that contains the “Hello…” string to be copied to persistent memory
size_t len - Size of memory to be copied
Step 4 – Display the “Hello…” string on screen
Step 5 – Close the pool
Use the pmemobj_close() function to close the pool. This function closes the persistent memory pool and deletes the pool handle pop. The object store lives on in the file we created. It can be re-opened at a later time using pmemobj_open() as described in the read_hello_string() function that we’ll create next.
pmemobj_close() has the following syntax:
void pmemobj_close(PMEMobjpool *pop);
PMEMobjpool *pop - Handle to persistent memory pool pop
The read_hello_string() Function
For the -r option, we’ll implement the read_hello_string() function, which will read from persistent memory in five steps. See the following code snippet for more details:
Step 1 – Open the pool
To read the “Hello…” string from persistent memory, we open our persistent memory pool with the pmemobj_open () function. It has the following syntax:
PMEMobjpool *pmemobj_open(const char *path, const char *layout);
const char *path
- Location of persistent memory pool fileconst char *layout
- Name for the object layout in the pool
Step 2 – Get root object
The usage here is the same as in the write_hello_string function.
Step 3 – Get a pointer to the root object
The usage here is the same as in the write_hello_string function.
However, here the program uses the pointer to read the “Hello…” string function and uses printf () to display it on screen.
Step 4 – Use regular C printf () to display the “Hello…” string on screen.
Step 5 – Close the pool
The usage here is the same as in the write_hello_string() function.
Compile and Run
Use the included Makefile to compile and build the binary. Running make will compile it in the current working directory:
Now run the program, where the file name is “t”:
Summary
In this “Hello…” sample code, we showed how to write and read a simple string to or from persistent memory. You can find this code sample and more persistent memory programming examples in the PMDK examples repository on our GitHub* repo.
About the Author
Thai Le is a software engineer focusing on cloud computing and performance computing analysis at Intel Corporation.
References
- “libpmemobj” library
- How to Emulate Persistent Memory Using Dynamic Random-access Memory (DRAM)
- Create a C++ Persistent Memory “Hello World” Program Using libpmemobj
- Create a “Hello World” Program Using Persistent Collections for Java* (PCJ)
- Create a “Hello World” Program Using the Low Level Persistence Library (LLPL) for Java*
- Introduction to Programming with Persistent Memory from Intel
- Persistent Memory -- Get Started
Notices
Twitter*: Allocate memory files and access objects in persistent memory files, called pools, in the PMDK. Creating, opening, closing, and copying is done using libpmemobj API functions.
Summary: Allocate memory files and access objects in persistent memory files, called pools, in the PMDK. Creating, opening, closing, and copying is done using libpmemobj API functions.