The Role of shm_open()
shm_open() is the entry point to POSIX shared memory. It either creates a new shared memory object or opens an existing one. The function looks and behaves very much like open() for regular files — on purpose. This design lets you use familiar file descriptor operations on shared memory objects.
The return value is a file descriptor. This fd is used with ftruncate() to set the object’s size, and with mmap() to map it into the process’s address space. After mapping, the fd can be closed without affecting the mapping.
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
int shm_open(const char *name, int oflag, mode_t mode);
/* Returns: file descriptor on success, -1 on error */
Three parameters:
"/myobject"
Must start with /
No other / allowed
Portable max length: NAME_MAX - 4
O_RDONLY — read only
O_RDWR — read + write
O_CREAT — create if absent
O_EXCL — fail if exists
O_TRUNC — reset to 0 size
chmod)
Only used when O_CREAT is set.
Common: 0600
(owner read+write)
The oflag parameter controls what happens depending on whether the object already exists:
| Flags | Object exists | Object absent | Use case |
|---|---|---|---|
O_RDWR |
Opens it | Error (ENOENT) | Reader/client opens existing |
O_RDWR | O_CREAT |
Opens it | Creates it | Writer, don’t care if it exists |
O_RDWR | O_CREAT | O_EXCL |
Error (EEXIST) | Creates it | Writer, strict — must be new |
O_RDONLY |
Opens read-only | Error (ENOENT) | Read-only consumer |
The name must follow specific rules to be portable across POSIX systems:
"/myshm"
"/sensor_data"
"/ipc_buffer_1"
"/app.lock"
Starts with /, no other slashes, reasonable length
"myshm" /* no leading / */
"/dir/myshm" /* embedded / */
"/" /* just a slash */
"" /* empty */
Missing /, embedded slashes, or empty string
Linux Detail: On Linux, "/myshm" maps to /dev/shm/myshm. You can verify with ls /dev/shm/ after creation.
The process that creates the shared memory object is the “writer” or “server”. It uses O_CREAT | O_RDWR and must call ftruncate() to give it a non-zero size.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#define SHM_NAME "/ep_demo"
#define SHM_SIZE 4096
int main(void)
{
int fd;
/* Step 1: Create the shared memory object */
fd = shm_open(SHM_NAME,
O_CREAT | O_RDWR | O_EXCL, /* create new, fail if exists */
S_IRUSR | S_IWUSR); /* owner read+write (0600) */
if (fd == -1) {
perror("shm_open");
exit(EXIT_FAILURE);
}
printf("Created shared memory object: %s (fd=%d)\n", SHM_NAME, fd);
/* Step 2: Set the size — object starts at 0 bytes */
if (ftruncate(fd, SHM_SIZE) == -1) {
perror("ftruncate");
shm_unlink(SHM_NAME); /* clean up before exit */
exit(EXIT_FAILURE);
}
printf("Set size to %d bytes\n", SHM_SIZE);
/* fd will be used with mmap() next — see next tutorial */
close(fd);
return 0;
}
Compile and run:
gcc creator.c -o creator -lrt
./creator
# Created shared memory object: /ep_demo (fd=3)
# Set size to 4096 bytes
ls -la /dev/shm/ep_demo
# -rw------- 1 ravi ravi 4096 Jun 12 10:00 ep_demo
The reader process opens the shared memory object that already exists. It does not need to call ftruncate() — the size was already set by the creator.
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#define SHM_NAME "/ep_demo"
int main(void)
{
int fd;
struct stat sb;
/* Open existing shared memory object — read-only */
fd = shm_open(SHM_NAME, O_RDONLY, 0);
if (fd == -1) {
perror("shm_open");
exit(EXIT_FAILURE);
}
/* Use fstat() to find the current size */
if (fstat(fd, &sb) == -1) {
perror("fstat");
exit(EXIT_FAILURE);
}
printf("Opened %s, size = %ld bytes\n", SHM_NAME, (long)sb.st_size);
/* Use fd with mmap() — see next tutorial */
close(fd);
return 0;
}
Note: fstat() works on the fd returned by shm_open() just like it does on a regular file fd. This is one of the advantages of the POSIX interface — standard tools work on shared memory.
Because shm_open() returns a regular fd, many standard system calls work on it:
| Operation | Use case on shm fd |
|---|---|
fstat(fd, &sb) |
Get current size, permissions, timestamps |
fchmod(fd, mode) |
Change access permissions |
ftruncate(fd, size) |
Set or resize the object |
mmap(NULL, size, ..., fd, 0) |
Map into address space |
close(fd) |
Close fd (mapping stays valid) |
| errno | Meaning | Fix |
|---|---|---|
ENOENT |
Object doesn’t exist and O_CREAT not set | Add O_CREAT or run creator first |
EEXIST |
O_CREAT | O_EXCL but object exists | shm_unlink() first, or remove O_EXCL |
EACCES |
No permission to open | Check object permissions with ls /dev/shm |
EINVAL |
Name is “/” or empty | Use valid name like “/myobj” |
EMFILE |
Process fd limit reached | Close unused fds |
Q1. What does shm_open() return and how is it used?
It returns a file descriptor, just like open(). This fd is then passed to ftruncate() to set the size and to mmap() to map the object. After mapping, the fd can be closed.
Q2. What is the initial size of a newly created shared memory object?
Zero bytes. You must call ftruncate(fd, size) to make it usable. Trying to mmap() a zero-size object will fail with EINVAL.
Q3. What is the difference between O_CREAT alone versus O_CREAT | O_EXCL?
O_CREAT alone creates the object if it doesn’t exist but silently succeeds if it already exists. O_CREAT | O_EXCL fails with EEXIST if the object already exists — it guarantees the caller is the creator.
Q4. Can you use read() and write() on the fd returned by shm_open()?
Technically yes on Linux, but this defeats the purpose. Shared memory is useful because you use mmap() and then access data directly as memory — pointer dereferences — without any system calls for the actual data transfer.
Q5. What happens if two processes both call shm_open with O_CREAT for the same name?
Both succeed — the second call opens the existing object. The size set by the first caller is preserved. If you want to detect a race, use O_CREAT | O_EXCL, which makes only one process succeed.
Q6. After closing the fd from shm_open(), does the mapping created by mmap() remain valid?
Yes. The mmap() call adds a reference to the underlying shared memory object. Closing the fd reduces the file descriptor count but does not destroy the mapping. The mapping stays valid until munmap() is called.
