In this lesson, you will learn how to design applications to treat persistent memory (PMEM) as a byte-addressable resource rather than a traditional block-based storage device.
Moving Beyond Block I/O
Historically, Linux has treated all storage as a series of blocks. When you save a file, the kernel breaks that data into chunks, manages a page cache, and sends I/O requests through a controller. This introduces significant latency because the CPU must wait for the storage bus to "seek" or transfer these blocks.
Persistent memory, however, is byte-addressable and stays at the speed of your memory bus. To leverage this, we use Persistent Memory File Abstractions. Instead of calling read() or write()—which forces the kernel to copy data between user-space buffers and the kernel page cache—we map the file directly into our application’s virtual address space.
The Direct Access (DAX) Approach
When you use a filesystem that supports Direct Access (DAX), the kernel maps the persistent memory media directly into your process. Once mapped, your application can read and write directly to that memory range as if it were regular RAM.
Consider this conceptual example of modifying a file in PMEM:
// Instead of read()/write() system calls:
int fd = open("/mnt/pmem/data", O_RDWR);
void *ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED_VALIDATE | MAP_SYNC, fd, 0);
// We perform a standard memory copy
memcpy(ptr, my_data, len);
// We use CPU instructions to ensure persistence (flush to the PMEM domain)
asm volatile("clwb %0" : : "m"(*(char *)ptr));
Why Adapt Application Design?
By using mmap with DAX, you bypass the kernel’s page cache entirely. This removes the "context switch" tax required for every I/O operation. However, you must now handle data consistency manually. Since you are writing directly to the media, if your program crashes mid-write, you might leave a partially updated file. You must design your application logic to include "memory barriers" or use atomic updates to ensure that data is safely committed to the persistent domain.
Key Takeaway: Persistent memory file abstractions shift the responsibility from the kernel I/O stack to the application, allowing byte-level access that eliminates block-based latency bottlenecks.