When shm_open() creates a brand new shared memory object, that object has zero length. You cannot map zero bytes, and even if you could, there would be nothing useful to work with.
You must call ftruncate() to set the object to your desired size before calling mmap(). Think of it as “allocating” the shared memory region.
#include <unistd.h>
int ftruncate(int fd, off_t length);
/* Returns: 0 on success, -1 on error */
fd is the file descriptor from shm_open(). length is the desired size in bytes.
If length is larger than the current size, the object is extended. New bytes are automatically initialized to zero. No garbage data.
If length is smaller than the current size, the object is truncated. Any process that still has a mapping beyond the new end will receive SIGBUS if it accesses the truncated region.
/*
* shm_ftruncate.c
* Demonstrates ftruncate() on a POSIX shared memory object:
* - Initial creation (size 0)
* - Setting initial size
* - Growing the object
* - Observing zero-initialization
*
* Compile: gcc shm_ftruncate.c -o shm_ftruncate -lrt
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <unistd.h>
#define SHM_NAME "/ep_truncate_demo"
int main(void)
{
int fd;
struct stat sb;
char *addr;
/* Step 1: Create โ starts at size 0 */
fd = shm_open(SHM_NAME, O_CREAT | O_EXCL | O_RDWR, 0600);
if (fd == -1) { perror("shm_open"); exit(EXIT_FAILURE); }
/* Verify size is 0 immediately after creation */
if (fstat(fd, &sb) == -1) { perror("fstat"); exit(EXIT_FAILURE); }
printf("After shm_open(): size = %lld bytes\n", (long long)sb.st_size);
/* Step 2: Set size to 1024 bytes */
if (ftruncate(fd, 1024) == -1) { perror("ftruncate"); exit(EXIT_FAILURE); }
fstat(fd, &sb);
printf("After ftruncate(1024): size = %lld bytes\n", (long long)sb.st_size);
/* Step 3: Map it and verify zero-initialization */
addr = mmap(NULL, 1024, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (addr == MAP_FAILED) { perror("mmap"); exit(EXIT_FAILURE); }
/* Check first 8 bytes are zero */
int all_zero = 1;
for (int i = 0; i < 8; i++) {
if (addr[i] != 0) { all_zero = 0; break; }
}
printf("First 8 bytes zero-initialized: %s\n", all_zero ? "YES" : "NO");
/* Write some data */
strcpy(addr, "EmbeddedPathashala");
/* Step 4: Grow the object to 2048 bytes */
if (ftruncate(fd, 2048) == -1) { perror("ftruncate grow"); exit(EXIT_FAILURE); }
fstat(fd, &sb);
printf("After ftruncate(2048): size = %lld bytes\n", (long long)sb.st_size);
/* The new region (bytes 1024โ2047) is also zero */
printf("Byte at offset 1024 (new region): %d\n", (unsigned char)addr[1024]);
/* Old data still intact after grow */
printf("Data after grow: %s\n", addr);
/* Cleanup */
munmap(addr, 2048);
close(fd);
shm_unlink(SHM_NAME);
return 0;
}
After shm_open(): size = 0 bytes
After ftruncate(1024): size = 1024 bytes
First 8 bytes zero-initialized: YES
After ftruncate(2048): size = 2048 bytes
Byte at offset 1024 (new region): 0
Data after grow: EmbeddedPathashala
You can call fstat() on the file descriptor from shm_open() to query metadata about the shared memory object. POSIX requires only certain fields to be meaningful; on Linux most fields work as you would expect for a file.
#include <sys/stat.h>
int fstat(int fd, struct stat *statbuf);
/* Returns: 0 on success, -1 on error */
/* Relevant fields in struct stat for shared memory: */
struct stat {
off_t st_size; /* Current size of the shared memory object in bytes */
mode_t st_mode; /* Permissions (e.g., 0600) โ changeable with fchmod() */
uid_t st_uid; /* Owner user ID */
gid_t st_gid; /* Owner group ID */
/* Linux also fills in time fields and other info */
};
fstat() to fill in st_size, st_mode, st_uid, and st_gid for shared memory objects. Linux fills in more fields (timestamps, etc.) but do not depend on those in portable code./*
* shm_fstat.c
* Open an existing shared memory object and print its metadata.
* Compile: gcc shm_fstat.c -o shm_fstat -lrt
* Run after creating a shm object with shm_creator (Part 2 example).
*/
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <unistd.h>
#include <time.h>
#define SHM_NAME "/ep_inspect"
int main(void)
{
int fd;
struct stat sb;
/* Create a 4096-byte object to inspect */
fd = shm_open(SHM_NAME, O_CREAT | O_RDWR, 0640);
if (fd == -1) { perror("shm_open"); exit(EXIT_FAILURE); }
if (ftruncate(fd, 4096) == -1) { perror("ftruncate"); exit(EXIT_FAILURE); }
/* Now inspect it with fstat */
if (fstat(fd, &sb) == -1) { perror("fstat"); exit(EXIT_FAILURE); }
printf("=== POSIX Shared Memory Object Info ===\n");
printf(" Name : %s\n", SHM_NAME);
printf(" Linux path : /dev/shm%s\n", SHM_NAME);
printf(" Size : %lld bytes\n", (long long)sb.st_size);
printf(" Permissions : %o (octal)\n", sb.st_mode & 0777);
printf(" Owner UID : %d\n", (int)sb.st_uid);
printf(" Owner GID : %d\n", (int)sb.st_gid);
/* Check if owner-readable/writable */
printf(" Owner can read : %s\n",
(sb.st_mode & S_IRUSR) ? "yes" : "no");
printf(" Owner can write: %s\n",
(sb.st_mode & S_IWUSR) ? "yes" : "no");
/* Change permissions with fchmod */
if (fchmod(fd, 0600) == -1) {
perror("fchmod");
} else {
printf(" fchmod(0600) applied.\n");
fstat(fd, &sb);
printf(" New permissions: %o\n", sb.st_mode & 0777);
}
close(fd);
shm_unlink(SHM_NAME);
return 0;
}
=== POSIX Shared Memory Object Info ===
Name : /ep_inspect
Linux path : /dev/shm/ep_inspect
Size : 4096 bytes
Permissions : 640 (octal)
Owner UID : 1000
Owner GID : 1000
Owner can read : yes
Owner can write: yes
fchmod(0600) applied.
New permissions: 600
In some designs, the creator grows the shared memory object over time as more data is needed. Here is the safe pattern for doing that:
/*
* Safe pattern for growing a shared memory object after initial mmap().
*
* Key rules:
* 1. Call ftruncate() to grow the object BEFORE accessing the new region.
* 2. If you already have a mmap() mapping, you must either:
* a. munmap() the old region and mmap() again with the new size, OR
* b. Use mremap() (Linux-specific) to extend the existing mapping.
* 3. Never access bytes beyond the current st_size โ that causes SIGBUS.
*/
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <unistd.h>
#define SHM_NAME "/ep_dynamic"
#define INITIAL_SIZE 1024
#define GROWN_SIZE 4096
int main(void)
{
int fd;
char *addr;
fd = shm_open(SHM_NAME, O_CREAT | O_EXCL | O_RDWR, 0600);
if (fd == -1) { perror("shm_open"); exit(EXIT_FAILURE); }
/* Set initial size and map */
ftruncate(fd, INITIAL_SIZE);
addr = mmap(NULL, INITIAL_SIZE, PROT_READ | PROT_WRITE,
MAP_SHARED, fd, 0);
if (addr == MAP_FAILED) { perror("mmap"); exit(EXIT_FAILURE); }
addr[0] = 'A'; /* Use initial region */
printf("Initial write OK. addr[0] = %c\n", addr[0]);
/* Now grow the object */
ftruncate(fd, GROWN_SIZE);
/* munmap old region and remap with new size */
munmap(addr, INITIAL_SIZE);
addr = mmap(NULL, GROWN_SIZE, PROT_READ | PROT_WRITE,
MAP_SHARED, fd, 0);
if (addr == MAP_FAILED) { perror("mmap (grown)"); exit(EXIT_FAILURE); }
/* Old data preserved, new region is zero */
printf("After grow: addr[0] = %c (preserved)\n", addr[0]);
printf("After grow: addr[2048] = %d (zero)\n", (unsigned char)addr[2048]);
munmap(addr, GROWN_SIZE);
close(fd);
shm_unlink(SHM_NAME);
return 0;
}
If you map N bytes but then shrink the underlying object with ftruncate() to fewer than N bytes, any access to the “orphaned” pages beyond the new end causes a SIGBUS signal. By default this kills the process.
munmap() before shrinking the object, or ensure all processes using the mapping are aware of the new boundary. Use fstat() to query the current size before accessing.mmap() cannot create a useful mapping of a zero-length object. ftruncate() sets the size to the desired number of bytes before mmap() is called.SIGBUS (bus error). This happens if you map N bytes but the underlying object is shrunk to fewer than N bytes by ftruncate(), and then a process accesses the now-invalid region.st_size (current size), st_mode (permissions), st_uid (owner user ID), and st_gid (owner group ID). Linux fills in additional fields (timestamps, etc.) but these are non-portable.fchmod(fd, new_mode) with the file descriptor from shm_open(). Similarly, fchown(fd, uid, gid) changes ownership.mmap() call specifies a fixed length. To access the new region, you must either munmap() and call mmap() again with the larger size, or use the Linux-specific mremap() to extend the existing mapping.SIGBUS. Coordinate shrinks carefully using synchronization.