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, we show how to create a “Hello World” program using the Persistent Memory Development Kit (PMDK) libpmem library. The libpmem library provides persistent memory support for memory mapping and flushing, making low-level persistent memory programming much easier. As you’ll see in the code below, programming to use persistent memory means using a memory-mapped file. Although this file can reside on any media (such as a regular hard disk drive or a solid-state drive), the best performance is obtained when the file resides in byte-addressable persistent memory. For example, you can use Intel® Optane™ DC persistent memory modules. If you cannot get your hands on those or other persistent memory products, you can emulate a persistent memory region using Dynamic Random-access Memory (DRAM).
Prerequisites
This article assumes that you have a basic understanding of persistent memory concepts and are familiar with the features of the PMDK. If not, visit Persistent Memory/Get Started on Intel® Developer Zone, where you will find the information you need to get started.
Code Sample Design
The sample creates a memory-mapped file, called a pool in PMDK terminology, where it will store the “Hello…” message. After the file is mapped into memory, the program continues, depending on the options supplied by the user. If the option is -r, then read_hello_string is called to read back the message written in persistent memory. If, on the other hand, -w is supplied, the program calls write_hello_string and uses strcpy to copy the “Hello…” message to persistent memory, as shown in the following code sample:
* hello_libpmem.c -- an example for libpmem library
*/
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <libpmem.h>
/* using 1k of pmem for this example */
#define PMEM_LEN 1024
// Maximum length of our buffer
#define MAX_BUF_LEN 30
/****************************
* This function writes the "Hello..." string to persistent memory.
*****************************/
void write_hello_string (char *buf, char *path)
{
char *pmemaddr;
size_t mapped_len;
int is_pmem;
/* create a pmem file and memory map it */
if ((pmemaddr = (char *)pmem_map_file(path, PMEM_LEN, PMEM_FILE_CREATE,0666, &mapped_len, &is_pmem)) == NULL) {
perror("pmem_map_file");
exit(1);
}
/* store a string to the persistent memory */
strcpy(pmemaddr, buf);
/* flush above strcpy to persistence */
if (is_pmem)
pmem_persist(pmemaddr, mapped_len);
else
pmem_msync(pmemaddr, mapped_len);
/* output a string from the persistent memory to console */
printf("\nWrite the (%s) string to persistent memory.\n", pmemaddr);
return;
}
/****************************
* This function reads the "Hello..." string from persistent memory.
*****************************/
void read_hello_string(char *path)
{
char *pmemaddr;
size_t mapped_len;
int is_pmem;
/* open the pmem file to read back the data */
if ((pmemaddr = (char *)pmem_map_file(path, PMEM_LEN, PMEM_FILE_CREATE,
0666, &mapped_len, &is_pmem)) == NULL) {
perror("pmem_map_file");
exit(1);
}
/* Reading the string from persistent-memory and write to console */
printf("\nRead the (%s) string from persistent memory.\n", pmemaddr);
return;
}
/****************************
* This main function gathers from the command line and calls the appropriate * functions to perform read and write persistently to memory.
*****************************/
int main(int argc, char *argv[])
{
char *path = argv[2];
// Create the string to save to persistent memory
char buf[MAX_BUF_LEN] = "Hello Persistent Memory!!!";
if (strcmp (argv[1], "-w") == 0) {
write_hello_string (buf, path);
}
else if (strcmp (argv[1], "-r") == 0) {
read_hello_string(path);
}
else {
fprintf(stderr, "Usage: %s <-w/-r> <filename>\n", argv[0]);
exit(1);
}
return 0;
}
Code Walk-through
In the main() function, we define an input array containing the text “Hello Persistent Memory!!!”.
// Create the string to save to persistent memory
char buf[MAX_BUF_LEN] = "Hello Persistent Memory!!!";
Next, the program parses the options passed to it and decides whether to read from or write to persistent memory.
The “write_hello_string” Function
This function completes the write to persistent memory in a few steps. See the following code snippet for details.
Create the Pool
/* create a pmem file and memory map it */
if ((pmemaddr = (char *)pmem_map_file(path, PMEM_LEN, PMEM_FILE_CREATE,0666, &mapped_len, &is_pmem)) == NULL) {
perror("pmem_map_file");
exit(1);
}
Create a persistent memory pool with pmem_map_file (), which has the syntax below:
void *pmem_map_file(const char *path, size_t len, int flags,
mode_t mode, size_t *mapped_lenp, int *is_pmemp);
- const char *path - Location of the memory pool file
- size_t len - Must be non-zero and specify the size of the file to be created
- int flags - Flags to be passed to the function. We pass PMEM_FILE_CREATE to force file creation.
- mode_t mode - Permission mode for the memory pool file
- size_t *mapped_lenp - The size of memory that was actually mapped
- int *is_pmemp - A flag indicating whether the file resides on a valid persistent memory device supporting DAX
The following code shows how the “Hello…” message is written to persistent memory:
/* store a string to the persistent memory */
strcpy(pmemaddr, buf);
/* flush above strcpy to persistence */
if (is_pmem)
pmem_persist(pmemaddr, mapped_len);
else
pmem_msync(pmemaddr, mapped_len);
As shown above, we use the strcpy function to copy the “Hello…” string to the persistent memory address location. However, we also need to ensure that the data is flushed from the CPU caches to make sure it is written all the way to persistent media. Use the is_pmem flag to determine whether to use the pmem_persist function to flush from userspace, or the pmem_msync function for context switching to the operating system and flushing a whole memory page.
Both the pmem_persist and pmem_msync functions use the same two parameters and have the following syntax:
void pmem_persist(const void *addr, size_t len);
int pmem_msync(const void *addr, size_t len);
- const void *addr - A pointer to the data that we want to flush, where the program uses pmemaddr.
- size_t len - The length of the data that we want to flush.
The “read_hello_string” Function
This function completes the read from persistent memory in one step. See the following code snippet for details.
Open the Pool
if ((pmemaddr = (char *)pmem_map_file(path, PMEM_LEN, PMEM_FILE_CREATE,
0666, &mapped_len, &is_pmem)) == NULL) {
perror("pmem_map_file");
exit(1);
}
This is the same function call that was used earlier in the write_hello_string function. We use it here to open the file to read back the string. Once it is opened, the “Hello…” string is accessed using the pmemaddr pointer. We then used the printf function to display the “Hello…” string on screen.
Compile and Run
Use the included Makefile to compile and build the binary. Running make compiles it in the current working directory, as shown:
$ make
After building the binary, the following examples show how to run it:
Write the (Hello Persistent Memory!!!) string to persistent memory.
$ ./hello_libpmem -w t
Read the (Hello Persistent Memory!!!) string from persistent memory.
$ ./hello_libpmem -r t
Bad input.
$ ./hello_libpmem -rw t
Usage: ./hello_libpmem <-w/-r> <filename>
Summary
This “Hello…” sample code, we showed how to use libpmem to write and read a simple string to or from persistent memory. The code sample included with this article is available in the GitHub repository linked above so that you can build and run the program yourself. You can find more persistent memory programming examples in the PMDK examples repository on our GitHub repository.
About the Author
Thai Le is a software engineer focusing on cloud computing and performance computing analysis at Intel Corporation.
References
“pmem_persist” and “pmem_msync” description
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