Quick Reference
This is a cheat sheet for quick lookup. Each section shows the essential syntax pattern you'll use repeatedly. For detailed explanations and context, see the linked tutorial sections.
Shared Memory Functions
These four functions form the complete lifecycle: create/get → attach → use → detach → remove.
| Function | Purpose | Returns |
|---|---|---|
shmget(key, size, flags) |
Create or get segment | shmid or -1 |
shmat(shmid, NULL, 0) |
Attach to address space | void* or (void*)-1 |
shmdt(addr) |
Detach from address space | 0 or -1 |
shmctl(shmid, IPC_RMID, NULL) |
Remove segment from kernel | 0 or -1 |
// Complete example
int shmid = shmget(0x1234, 1024, 0666 | IPC_CREAT); // Create 1KB segment
void* addr = shmat(shmid, NULL, 0); // Attach
// ... use memory ...
shmdt(addr); // Detach
shmctl(shmid, IPC_RMID, NULL); // Remove
Semaphore Functions
Create a set of semaphores, perform P/V operations, set values, and remove when done.
| Function | Purpose | Returns |
|---|---|---|
semget(key, nsems, flags) |
Create or get semaphore set | semid or -1 |
semop(semid, &sembuf, 1) |
Perform P or V operation | 0 or -1 |
semctl(semid, num, SETVAL, arg) |
Set semaphore value | 0 or -1 |
semctl(semid, 0, IPC_RMID) |
Remove semaphore set | 0 or -1 |
// Complete example
int semid = semget(0x5678, 3, 0666 | IPC_CREAT); // Create 3 semaphores
union semun arg; arg.val = 1;
semctl(semid, 0, SETVAL, arg); // Set semaphore 0 to 1
// ... use semaphores ...
semctl(semid, 0, IPC_RMID); // Remove
Semaphore Operation (P and V)
The sembuf structure tells semop() which semaphore to operate on and how:
struct sembuf op;
op.sem_num = 0; // Which semaphore in the set (0, 1, 2, ...)
op.sem_op = -1; // Operation: -1 = P (wait/decrement)
// +1 = V (signal/increment)
op.sem_flg = 0; // 0 = blocking, IPC_NOWAIT = non-blocking
semop(semid, &op, 1);
sem_op Value Quick Reference:
| Value | Operation | Behavior |
|---|---|---|
-1 |
P (wait/decrement) | Block if value is 0, then decrement |
+1 |
V (signal/increment) | Increment immediately (never blocks) |
0 |
Wait for zero | Block until value becomes 0 |
-N |
Acquire N resources | Block if value < N, then decrement by N |
+N |
Release N resources | Increment by N immediately |
📘 Note: While ±1 is most common, sem_op can be any integer. Use
-3 to acquire 3 resources atomically, or +2 to release 2 at once.
Memory Layout Pattern
When placing multiple data structures in shared memory, use pointer arithmetic:
// Calculate total size needed
int size = sizeof(header_t) + count * sizeof(item_t);
// Get and attach
void *base = shmat(shmid, NULL, 0);
// Cast to access header at the start
header_t *header = (header_t*)base;
// Array starts after header (cast through char* for byte arithmetic)
item_t *items = (item_t*)((char*)base + sizeof(header_t));
Circular Buffer Index
The modulo operator wraps the index around when it reaches the buffer size:
index = (index + 1) % buffer_size;
// Example with buffer_size = 4:
// index: 0 → 1 → 2 → 3 → 0 → 1 → 2 → 3 → 0 ...
Error Checking Pattern
Always check return values and use errno for the error reason:
#include <errno.h>
#include <string.h>
int result = some_syscall(...);
if (result == -1) {
fprintf(stderr, "Error: %s\n", strerror(errno));
exit(EXIT_FAILURE);
}
// For shmat, check against (void*)-1, not -1:
void* addr = shmat(shmid, NULL, 0);
if (addr == (void*)-1) {
fprintf(stderr, "shmat failed: %s\n", strerror(errno));
exit(EXIT_FAILURE);
}
Common errno Values:
| errno | Meaning |
|---|---|
ENOENT |
Resource doesn't exist (consumer not running?) |
EACCES |
Permission denied |
EEXIST |
Resource already exists (with IPC_EXCL) |
EINVAL |
Invalid argument (wrong size, bad ID) |
EINTR |
Interrupted by signal (retry the call) |
Signal Handler Pattern
For graceful cleanup on Ctrl+C:
#include <signal.h>
// Global flag - MUST be sig_atomic_t for safety
static volatile sig_atomic_t g_running = 1;
// Handler just sets the flag (no complex operations!)
void handler(int sig) {
(void)sig; // Suppress unused warning
g_running = 0;
}
// In main():
struct sigaction sa;
sa.sa_handler = handler;
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0; // No SA_RESTART - let semop return with EINTR
sigaction(SIGINT, &sa, NULL); // Ctrl+C
sigaction(SIGTERM, &sa, NULL); // kill command
while (g_running) {
// Main loop - will exit when signal sets g_running = 0
}
cleanup_resources();
Shell Commands
Inspect and remove IPC resources from the command line:
# Inspection
ipcs # List all IPC resources
ipcs -m # List shared memory only
ipcs -s # List semaphores only
# Removal by ID
ipcrm -m <shmid> # Remove shared memory segment
ipcrm -s <semid> # Remove semaphore set
# Removal by KEY
ipcrm -M <key> # Remove shared memory by key
ipcrm -S <key> # Remove semaphore set by key
# Remove all (use with caution!)
ipcrm -a # Remove all IPC for current user
Producer-Consumer Algorithm
The classic three-semaphore solution. Order matters!
Producer:
P(empty) // 1. Wait for empty slot (blocks if buffer full)
P(mutex) // 2. Enter critical section
// ... write to buffer ...
V(mutex) // 3. Exit critical section
V(full) // 4. Signal item available
Consumer:
P(full) // 1. Wait for item (blocks if buffer empty)
P(mutex) // 2. Enter critical section
// ... read from buffer ...
V(mutex) // 3. Exit critical section
V(empty) // 4. Signal slot available
Initial Semaphore Values:
| Semaphore | Initial Value | Meaning |
|---|---|---|
mutex |
1 | Unlocked (1 process can enter) |
empty |
buffer_size | All slots empty |
full |
0 | No items available yet |
External Resources
System V IPC Documentation
- Oracle System V IPC Guide — Comprehensive official documentation
- GeeksforGeeks: IPC Shared Memory — Beginner-friendly examples
- TutorialsPoint: IPC Semaphores — Step-by-step tutorials
Producer-Consumer Problem
- Producer-Consumer with Semaphores — Algorithm deep dive
- Producer-Consumer with Shared Memory — Complete implementation guide
C++ and System Programming
- C++ normal_distribution — Random number generation reference
- Linux: clock_gettime — High-resolution time functions
- Terminal Colored Output — ANSI escape codes for formatting
Linux Man Pages (run in terminal)
man shmget,man shmat,man shmdt,man shmctl— Shared memory functionsman semget,man semop,man semctl— Semaphore functionsman ipcs,man ipcrm— IPC inspection and removal utilities
Summary: What You've Learned
This tutorial covered the complete System V IPC stack for building a producer-consumer system:
- Part 1 - Core Concepts: IPC problem, producer-consumer pattern, three-semaphore solution, circular buffers
- Part 2 - Shared Memory: shmget, shmat, shmdt, shmctl — the complete lifecycle
- Part 3 - Semaphores: semget, semop, semctl, struct sembuf, union semun — synchronization primitives
- Part 4 - C Essentials: Pointers, pointer arithmetic, structs, string safety, random numbers
- Part 5 - Implementation: Complete consumer and producer code with signal handlers
- Part 6 - Best Practices: Error handling, EINTR, cleanup, common pitfalls
The producer-consumer pattern is fundamental in systems programming. Understanding how shared memory and semaphores work together gives you the foundation for building more complex concurrent systems like databases, message brokers, and high-performance servers.
📁 Full Source Code: Available in the code/ folder with complete error
handling and comments.