Direct pointer access to memory shared between processes — the fastest IPC mechanism.
Every IPC mechanism we've seen so far involves copying data: from one process into the kernel (write/msgsnd), then from the kernel into another process (read/msgrcv). That's two copies per message. For high-performance applications — games, databases, real-time systems — this overhead adds up.
Shared memory eliminates the copies entirely. Both processes get a pointer to the same physical memory. When Process A writes to that pointer, Process B sees the change instantly. No kernel involvement for the data transfer itself.
Click "Write" to have Process A modify the shared segment. Process B sees changes instantly.
Just like message queues and semaphores, shared memory uses the same System V pattern: ftok() for the key, then a get function to create or connect.
c #include <sys/shm.h> int shmget(key_t key, size_t size, int shmflg);
| Argument | Purpose |
|---|---|
key | Unique key from ftok() |
size | Size in bytes of the shared memory segment |
shmflg | Permissions OR'd with IPC_CREAT |
c key_t key; int shmid; key = ftok("/home/beej/somefile3", 'R'); shmid = shmget(key, 1024, 0644 | IPC_CREAT);
This creates a 1K shared memory segment with rw-r--r-- permissions. The shmid is just a handle — you can't use it as a pointer yet. For that, you need shmat().
This is the magic step. shmat() takes your segment ID and returns an actual pointer you can use like any other C pointer:
c void *shmat(int shmid, void *shmaddr, int shmflg);
| Argument | Purpose |
|---|---|
shmid | Segment ID from shmget() |
shmaddr | Where to map it (set to 0 to let the OS choose) |
shmflg | SHM_RDONLY for read-only, or 0 for read-write |
c char *data; data = shmat(shmid, (void *)0, 0); if (data == (char *)(-1)) perror("shmat");
(char *)(-1), not NULL.Once you have the pointer, you can read and write to it like any memory. The pointer is just a char * (or whatever type you cast it to). You're directly touching the shared segment.
Once you have the pointer from shmat(), reading and writing is trivially easy. It's just normal C pointer operations:
c char *data = shmat(shmid, (void *)0, 0); /* Write to the segment */ printf("Enter a string: "); fgets(data, 1024, stdin); /* Read from the segment */ printf("Segment contains: %s\n", data);
That's it. No special system calls for reading or writing. data behaves exactly like any other pointer. You can treat it as a char *, a pointer to a struct, an array — whatever you need.
When you're done with the shared memory segment, there are two separate steps: detach (disconnect your process) and delete (remove the segment from the system).
c /* Detach: disconnect from the segment */ shmdt(data); /* pass the pointer from shmat() */ /* Delete: remove the segment from the kernel */ shmctl(shmid, IPC_RMID, NULL);
shmctl(shmid, IPC_RMID, NULL). And even then, it waits until all processes have detached before actually freeing the memory.| Operation | Function | Effect |
|---|---|---|
| Detach | shmdt(ptr) | This process can no longer access the segment |
| Delete | shmctl(id, IPC_RMID, NULL) | Segment will be destroyed after all processes detach |
After shmdt(), any access through the old pointer will cause a segmentation fault. The pointer is dangling.
ipcs -m to list them and ipcrm -m <shmid> to delete them. Always clean up after yourself.Here's the elephant in the room: shared memory has no built-in synchronization. If two processes write to the same address at the same time, you get a data race. The result is undefined — corrupted data, partial writes, nonsense values.
The solution: use semaphores (from Chapter 8) to protect access:
c /* Lock the semaphore before writing */ sb.sem_op = -1; semop(semid, &sb, 1); /* Write to shared memory (critical section) */ strcpy(data, "Hello World"); /* Unlock the semaphore */ sb.sem_op = 1; semop(semid, &sb, 1);
Beej's demo program reads or writes to a shared memory segment depending on command-line arguments. Run it with an argument to store data, without to read it:
c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/types.h> #include <sys/ipc.h> #include <sys/shm.h> #define SHM_SIZE 1024 int main(int argc, char *argv[]) { key_t key; int shmid; char *data; key = ftok("shmdemo.c", 'R'); shmid = shmget(key, SHM_SIZE, 0644 | IPC_CREAT); data = shmat(shmid, (void *)0, 0); if (data == (char *)(-1)) { perror("shmat"); exit(1); } if (argc == 2) { printf("writing to segment: \"%s\"\n", argv[1]); strncpy(data, argv[1], SHM_SIZE); } else { printf("segment contains: \"%s\"\n", data); } shmdt(data); return 0; }
shmdemo "Hello, shared memory!" to write. Then run shmdemo (no args) to read. The data persists between runs because the segment lives in the kernel. Use ipcrm to delete when done.Watch two processes read and write to the same memory region. Without synchronization, simultaneous writes can corrupt data.
Click to have processes write values into the shared segment. Both see the same memory.
Shared memory is the fastest IPC because there's no kernel involvement for actual data transfer. But it requires careful synchronization.
| Feature | Pipes/FIFOs | Message Queues | Shared Memory |
|---|---|---|---|
| Speed | Medium (2 copies) | Medium (2 copies) | Fast (0 copies) |
| Sync | Built-in (blocking) | Built-in (blocking) | Manual (semaphores) |
| Data model | Byte stream | Discrete messages | Raw memory |
| Direction | One-way | One-way (per queue) | Any |
| Persistence | Process lifetime | Kernel-persistent | Kernel-persistent |
| Cleanup | Automatic | Manual (ipcrm) | Manual (ipcrm) |
"The cool thing about shared memory segments is that they are what they sound like: a segment of memory that is shared between processes." — Beej