IPC via Shared Memory
POSIX / SUSv3
shm_open, mmap
POSIX shared memory is a mechanism that allows two or more processes to share a region of physical memory. It is one of the fastest forms of Inter-Process Communication (IPC) because data does not need to be copied between processes — they both map the same physical pages into their own virtual address space and read/write directly.
Unlike System V shared memory (the older API), POSIX shared memory uses a cleaner file-descriptor-based interface. A shared memory object is identified by a name (like /demo_shm) and behaves like a file that lives in RAM.
Two processes map the same shared memory object into their virtual address space:
|
Process A (Writer)
Virtual Address Space
Mapped Region
addr → physical page memcpy(addr, data, len)
|
Physical RAM
/demo_shm
shared pages |
Process B (Reader)
Virtual Address Space
Mapped Region
addr → same physical page write(STDOUT, addr, len)
|
Both processes call shm_open() to get a file descriptor, then mmap() to map it into memory. After that, they access shared data directly through the pointer — no syscall needed per access.
shm_open() is the entry point. It creates a new shared memory object or opens an existing one. It returns a regular file descriptor.
#include <fcntl.h>
#include <sys/mman.h>
int shm_open(const char *name, int oflag, mode_t mode);
/* Returns: file descriptor on success, -1 on error */
| Parameter | Description |
|---|---|
name |
Object name — must start with /, e.g., /my_shm |
oflag |
O_RDONLY, O_RDWR, optionally OR’d with O_CREAT, O_EXCL, O_TRUNC |
mode |
Permission bits (like 0600), used only when O_CREAT is specified |
Important flags:
O_CREAT— Create the object if it does not existO_EXCL— Combined withO_CREAT: fail if object already exists (atomic check+create)O_RDWR— Open for both reading and writingO_RDONLY— Open for reading onlyO_TRUNC— Truncate the object to zero length if it already exists
On Linux, shared memory objects appear as files under /dev/shm/. The leading / in the name is stripped when creating the file there.
This example creates a shared memory object named /myshm, sets its size to 4096 bytes, and maps it into the process address space:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <unistd.h>
#define SHM_NAME "/myshm"
#define SHM_SIZE 4096
int main(void)
{
int fd;
void *addr;
/* Step 1: Create shared memory object */
fd = shm_open(SHM_NAME, O_CREAT | O_RDWR, 0600);
if (fd == -1) {
perror("shm_open");
exit(EXIT_FAILURE);
}
/* Step 2: Set the size of the object */
if (ftruncate(fd, SHM_SIZE) == -1) {
perror("ftruncate");
exit(EXIT_FAILURE);
}
/* Step 3: Map it into virtual address space */
addr = mmap(NULL, SHM_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (addr == MAP_FAILED) {
perror("mmap");
exit(EXIT_FAILURE);
}
/* Step 4: Use it — write a string */
strncpy((char *)addr, "Hello from shared memory!", SHM_SIZE);
printf("Written: %s\n", (char *)addr);
/* Step 5: Unmap and close fd (object still exists!) */
munmap(addr, SHM_SIZE);
close(fd);
printf("Shared memory object /dev/shm/myshm still exists.\n");
printf("Run: cat /dev/shm/myshm\n");
return 0;
}
Compile and run:
gcc -o shm_create shm_create.c -lrt
./shm_create
ls -l /dev/shm/
Note: Link with -lrt on older Linux systems. On modern glibc, it may not be needed.
- Name must begin with
/ - Name should contain only one
/(at the start) for portability - Name cannot be
/alone - On Linux: stored as a file in
/dev/shm/
Valid names:
/my_shm
/demo_shm
/app123_buffer
Avoid (non-portable):
/dir/my_shm /* Multiple slashes — not portable */
| Step | Call | Purpose |
|---|---|---|
| 1 | shm_open(name, O_CREAT|O_RDWR, 0600) |
Create or open the shared memory object; get fd |
| 2 | ftruncate(fd, size) |
Set the size (new objects start at zero bytes) |
| 3 | mmap(NULL, size, PROT, MAP_SHARED, fd, 0) |
Map it into the process address space |
| 4 | close(fd) |
Close fd (mapping stays active) |
| 5 | /* use addr directly */ |
Read/write via the mapped pointer |
| 6 | munmap(addr, size) |
Unmap from this process |
| 7 | shm_unlink(name) |
Remove the object (when no longer needed) |
SUSv3 mandates that POSIX shared memory objects have kernel persistence: they survive after all processes that created or mapped them have exited. The object remains in the system until:
- A process explicitly calls
shm_unlink(), or - The system is rebooted
This is different from anonymous mmap() memory, which disappears when the process exits. Because of kernel persistence, always remember to shm_unlink() when done to avoid resource leaks.
/* Always clean up! */
shm_unlink("/myshm"); /* Removes /dev/shm/myshm */
If two processes both try to create the same shared memory object, only one should succeed. Use O_EXCL for atomic check-and-create:
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <errno.h>
int main(void)
{
int fd = shm_open("/exclusive_shm", O_CREAT | O_EXCL | O_RDWR, 0600);
if (fd == -1) {
if (errno == EEXIST) {
printf("Shared memory already exists. Opening existing...\n");
fd = shm_open("/exclusive_shm", O_RDWR, 0);
if (fd == -1) {
perror("shm_open existing");
exit(EXIT_FAILURE);
}
} else {
perror("shm_open");
exit(EXIT_FAILURE);
}
} else {
printf("Created new shared memory object.\n");
ftruncate(fd, 1024);
}
/* ... use fd ... */
close(fd);
return 0;
}
| Feature | POSIX | System V |
|---|---|---|
| Identifier | Name string (e.g., /myshm) |
Integer key (key_t) |
| Create API | shm_open() |
shmget() |
| Map API | mmap() |
shmat() |
| Unmap API | munmap() |
shmdt() |
| Delete API | shm_unlink() |
shmctl(IPC_RMID) |
| Interface style | File descriptor (modern) | Integer ID (older) |
| Visibility | /dev/shm/ filesystem |
ipcs -m command |
Q1. What is POSIX shared memory and how does it differ from pipes or message queues?
POSIX shared memory allows multiple processes to map the same physical memory pages into their virtual address spaces. Unlike pipes or message queues, data does not pass through the kernel on each access — processes read and write directly to the mapped region. This makes it the fastest IPC mechanism, but it requires explicit synchronization (e.g., semaphores) since there is no built-in ordering or locking.
Q2. What does shm_open() return and what does it NOT do?
shm_open() returns a regular file descriptor referring to the shared memory object. It does NOT set the size of the object — a new object always starts with size zero. You must call ftruncate() to give it a non-zero size before mapping.
Q3. Why is ftruncate() necessary after shm_open(O_CREAT)?
A newly created shared memory object has size zero. Attempting to mmap() a zero-size object gives a zero-length mapping which is useless. ftruncate(fd, size) extends the object to the desired size, allocating the backing memory pages.
Q4. What is kernel persistence for shared memory?
Kernel persistence means the shared memory object survives after all processes that created or used it have exited. It remains until explicitly deleted with shm_unlink() or the system reboots. This is important to remember — forgetting to unlink causes resource leaks that persist across process restarts.
Q5. What is the purpose of O_EXCL in shm_open()?
When combined with O_CREAT, O_EXCL makes the creation atomic: the call succeeds only if the object does not already exist. If it does exist, shm_open() returns -1 with errno set to EEXIST. This prevents two processes from accidentally creating the same object simultaneously.
Q6. Where are POSIX shared memory objects stored on Linux?
On Linux, they are stored as files in the /dev/shm/ directory, which is a tmpfs filesystem backed by RAM. You can inspect them with ls -l /dev/shm/ and even use cat on them.
Q7. Can you use standard file operations (read, write, lseek) on the fd returned by shm_open()?
Yes, technically you can use read(), write(), lseek(), and ftruncate() on it since it is a regular file descriptor. However, the normal usage pattern is to call mmap() to get a pointer and then access memory directly, which avoids the overhead of system calls on each access.
Q8. What happens to existing mappings when shm_unlink() is called?
Existing mappings in all processes remain valid. shm_unlink() only removes the name from the filesystem, preventing new shm_open() calls from opening the object. Once all processes have called munmap() or exited, the underlying memory is freed. This is the same behavior as unlink() on regular files.
Next: Writing data into shared memory with ftruncate and memcpy
