The shmget() System Call
shmget() is the first step in using System V shared memory. It either creates a new segment or opens an existing one, returning an integer identifier called shmid (shared memory ID). This shmid is used in all subsequent calls (shmat, shmdt, shmctl).
#include <sys/ipc.h>
#include <sys/shm.h>
int shmget(key_t key, size_t size, int shmflg);
/* Returns: shmid (non-negative int) on success, -1 on error */
| Parameter | Type | Purpose |
|---|---|---|
key |
key_t (integer) |
Name used to identify the segment. Unrelated processes use the same key to find the same segment. |
size |
size_t |
Size in bytes. Rounded up to next page boundary by kernel. Ignored (use 0) when opening existing segment. |
shmflg |
int (flags + permissions) |
Combination of permission bits (e.g. 0660) and flags like IPC_CREAT, IPC_EXCL. |
The key is how unrelated processes agree on which shared memory segment to use. There are three strategies:
1. Hardcoded integer key (simplest)
#define MY_KEY 0xDEAD1234 /* Pick a unique integer constant */
shmid = shmget(MY_KEY, SHM_SIZE, IPC_CREAT | 0660);
Simple, but there is a risk of collision with other programs using the same key.
2. ftok() — derive key from a file path (recommended)
#include <sys/ipc.h>
key_t key = ftok("/tmp/myapp", 'A');
/* ftok combines the file's inode number + device number + proj_id ('A')
to produce a (usually) unique key_t */
if (key == -1) {
perror("ftok");
exit(EXIT_FAILURE);
}
shmid = shmget(key, SHM_SIZE, IPC_CREAT | 0660);
The file at the given path must exist. Both writer and reader call ftok() with the same path and project ID to get the same key.
3. IPC_PRIVATE — anonymous segment for related processes
/* IPC_PRIVATE creates a new unique segment every time.
Use for parent-child communication after fork(). */
shmid = shmget(IPC_PRIVATE, SHM_SIZE, 0660);
/* Child inherits shmid after fork(), so both can call shmat() */
pid_t pid = fork();
if (pid == 0) {
/* Child: attach and use segment */
void *shmp = shmat(shmid, NULL, 0);
/* ... */
}
With IPC_PRIVATE, the key is 0. Each call creates a brand-new segment — there is no way for an unrelated process to find it by key.
| Flag | Meaning | Typical Use |
|---|---|---|
IPC_CREAT |
Create segment if it doesn’t exist. If it exists, just open it. | Server/writer startup |
IPC_EXCL |
Used with IPC_CREAT: fail with EEXIST if segment already exists. | Ensure you’re the creator |
0660 (or similar) |
Permission bits: owner read/write, group read/write, others none. | Access control |
SHM_HUGETLB |
Use huge pages (Linux-specific, requires kernel support). | High-performance large segments |
Permission bits work like file permissions. The octal value 0660 means owner and group can read/write; others cannot access. Always OR the permission bits with the flags:
/* Creator: IPC_CREAT + permissions */
shmid = shmget(key, size, IPC_CREAT | 0660);
/* Open existing only (no IPC_CREAT): */
shmid = shmget(key, 0, 0); /* size=0, flags=0 */
/* Create exclusively (fail if already exists): */
shmid = shmget(key, size, IPC_CREAT | IPC_EXCL | 0660);
The kernel rounds the requested size up to the next multiple of the system page size (typically 4096 bytes). So if you ask for 100 bytes, the kernel allocates one full page (4096 bytes). The extra bytes are accessible but you should not use them — their contents are unspecified.
#include <stdio.h>
#include <unistd.h>
#include <sys/shm.h>
int main(void)
{
long page_size = sysconf(_SC_PAGESIZE);
printf("Page size: %ld bytes\n", page_size);
/* Request 100 bytes — kernel will allocate 4096 (one page) */
int shmid = shmget(IPC_PRIVATE, 100, IPC_CREAT | 0660);
struct shmid_ds ds;
shmctl(shmid, IPC_STAT, &ds);
printf("Requested: 100 bytes\n");
printf("Actual segment size: %zu bytes\n", ds.shm_segsz);
shmctl(shmid, IPC_RMID, NULL);
return 0;
}
/* Output:
Page size: 4096 bytes
Requested: 100 bytes
Actual segment size: 100 bytes (kernel stores original request)
But physical allocation is 4096 bytes */
Note: shm_segsz stores the originally requested size, not the rounded-up physical allocation. The real allocated physical size is always a multiple of the page size.
| errno | Cause | Fix |
|---|---|---|
EEXIST |
IPC_CREAT | IPC_EXCL used, but segment already exists | Remove old segment with ipcrm -m shmid |
ENOENT |
No IPC_CREAT but segment doesn’t exist | Run creator first, or add IPC_CREAT |
EINVAL |
Requested size less than SHMMIN or more than SHMMAX, or existing segment is smaller than requested | Check system limits with ipcs -l |
ENOMEM |
Not enough memory to create segment | Reduce segment size or free system memory |
ENOSPC |
System-wide limit on number of segments (SHMMNI) reached | Delete old segments with ipcrm |
EACCES |
Permission denied (wrong user/group) | Check permissions with ipcs -m |
/* Robust shmget with error diagnosis */
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/shm.h>
int create_shm(key_t key, size_t size)
{
int shmid = shmget(key, size, IPC_CREAT | IPC_EXCL | 0660);
if (shmid == -1) {
if (errno == EEXIST) {
fprintf(stderr, "Segment already exists — trying to open it\n");
shmid = shmget(key, 0, 0); /* open existing */
} else {
perror("shmget");
exit(EXIT_FAILURE);
}
}
return shmid;
}
These commands are very useful during development and debugging:
# List all System V shared memory segments
ipcs -m
# Output example:
# ------ Shared Memory Segments --------
# key shmid owner perms bytes nattch status
# 0x00001234 196608 ravi 660 1024 0
# Delete a specific segment
ipcrm -m <shmid>
# Delete by key
ipcrm -M 0x1234
# Delete ALL shared memory segments owned by you (dangerous!)
ipcs -m | awk 'NR>3 {print $2}' | xargs -I{} ipcrm -m {}
# Check system limits for shared memory
ipcs -l
# Shows SHMMAX (max segment size), SHMMIN, SHMMNI (max segments), etc.
Common debugging scenario: Your program crashed without cleaning up — the shared memory segment is still there. The next run gets EEXIST. Use ipcs -m to find the stale segment and ipcrm -m <shmid> to delete it.
#include <stdio.h>
#include <stdlib.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#define SHM_SIZE 4096
int main(int argc, char *argv[])
{
key_t key;
int shmid;
struct shmid_ds ds;
/* Generate key from this program's own path */
key = ftok(argv[0], 'S');
if (key == -1) {
perror("ftok");
exit(EXIT_FAILURE);
}
printf("Generated key: 0x%x\n", key);
/* Create the segment */
shmid = shmget(key, SHM_SIZE, IPC_CREAT | IPC_EXCL | 0660);
if (shmid == -1) {
perror("shmget create");
exit(EXIT_FAILURE);
}
printf("Created shmid: %d\n", shmid);
/* Retrieve segment information */
if (shmctl(shmid, IPC_STAT, &ds) == -1) {
perror("shmctl IPC_STAT");
exit(EXIT_FAILURE);
}
printf("Segment size : %zu bytes\n", ds.shm_segsz);
printf("Creator PID : %d\n", ds.shm_cpid);
printf("Permissions : %o\n", ds.shm_perm.mode);
printf("Attach count : %lu\n", (unsigned long)ds.shm_nattch);
/* Clean up */
if (shmctl(shmid, IPC_RMID, NULL) == -1) {
perror("shmctl IPC_RMID");
exit(EXIT_FAILURE);
}
printf("Segment deleted.\n");
return 0;
}
Q1. What is the difference between IPC_CREAT and IPC_CREAT | IPC_EXCL?
IPC_CREAT alone: create the segment if it doesn’t exist; if it already exists, just return its shmid (no error). IPC_CREAT | IPC_EXCL: create only if it doesn’t exist; if it already exists, fail with EEXIST. The combination guarantees you are the creator.
Q2. What does ftok() do and why is it useful?
ftok(path, proj_id) derives a key from the file’s inode number and device number combined with a project ID byte. This allows unrelated processes to independently compute the same key as long as they agree on the path and project ID, without hardcoding a magic integer. The file must exist when ftok() is called.
Q3. When would you use IPC_PRIVATE?
When the communicating processes are related (parent-child). The parent creates the segment with IPC_PRIVATE, forks, and the child inherits the shmid. There is no need for a shared key because the shmid is passed through the process relationship. Unrelated processes cannot find a IPC_PRIVATE segment.
Q4. What is the significance of the size parameter when opening an existing segment?
When opening an existing segment (no IPC_CREAT), you should pass size = 0. If you pass a non-zero size larger than the existing segment, shmget() fails with EINVAL. Passing 0 means “I’ll accept whatever size it already is.”
Q5. A process crashes without deleting its shared memory segment. What happens?
The segment persists in the kernel indefinitely until the system is rebooted or someone explicitly deletes it with shmctl(IPC_RMID) or the ipcrm command. This is why production programs should install signal handlers (for SIGTERM, SIGINT) that clean up IPC resources before exiting.
