POSIX Shared Memory: shm_open
Shared memory is the fastest form of Inter-Process Communication (IPC) because it allows multiple processes to access the same region of physical memory. In standard POSIX, shared memory is created or opened using shm_open, which returns a file descriptor.
The process then resizes the shared memory object using ftruncate and maps it into its address space using mmap.
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int create_shared_memory(const char* name, size_t size) {
int fd = shm_open(name, O_CREAT | O_RDWR, 0666);
if (fd == -1) return -1;
if (ftruncate(fd, size) == -1) {
close(fd);
return -1;
}
return fd;
}
mmap for Shared Memory
The mmap function is the core of memory mapping. To map a shared memory region, the MAP_SHARED flag must be used. Modifications made to the mapped region are visible to all other processes that have mapped the same region.
void* map_shared_memory(int fd, size_t size) {
void* ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
return (ptr == MAP_FAILED) ? NULL : ptr;
}
Android ashmem and memfd
In AOSP, standard POSIX shared memory was historically less common due to Android's strict isolation and the risk of memory leaks if a process crashed without unlinking the shared object.
Android introduced ashmem (Anonymous Shared Memory). Ashmem regions are represented by file descriptors but exist only in memory. They automatically disappear when the last file descriptor is closed, preventing leaks. Furthermore, ashmem allows the kernel to reclaim unpinned memory regions when the system is under memory pressure.
#include <cutils/ashmem.h>
#include <sys/mman.h>
int create_ashmem_region(const char* name, size_t size) {
int fd = ashmem_create_region(name, size);
if (fd < 0) return -1;
return fd;
}
Modern Android (starting from API level 29) has transitioned towards the standard Linux memfd_create system call, deprecating ashmem to align more closely with the upstream Linux kernel. memfd_create creates an anonymous file that behaves like a regular file but lives purely in RAM.
#include <sys/mman.h>
#include <linux/memfd.h>
#include <sys/syscall.h>
#include <unistd.h>
int create_memfd(const char* name) {
return syscall(SYS_memfd_create, name, MFD_CLOEXEC);
}
Synchronization Over Shared Memory
Because multiple processes can read and write to the same memory addresses concurrently, synchronization mechanisms are mandatory to avoid race conditions.
Standard mechanisms include placing POSIX mutexes (pthread_mutex_t) directly inside the shared memory region. The mutex must be initialized with the PTHREAD_PROCESS_SHARED attribute.
#include <pthread.h>
struct SharedData {
pthread_mutex_t mutex;
int counter;
};
void init_shared_mutex(SharedData* data) {
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED);
pthread_mutex_init(&data->mutex, &attr);
pthread_mutexattr_destroy(&attr);
}
In Android, std::atomic variables mapped into shared memory are often used for lock-free IPC designs, such as in the AudioFlinger or SurfaceFlinger paths, where strict low-latency constraints exist.