Quick Reference

📘 How to Use This Page

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

→ Full details in Part 2: Shared Memory

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

→ Full details in Part 3: Semaphores

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));

→ Why cast through char*? See C Essentials

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();

→ EINTR handling details in Best Practices

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

→ Full explanation in Part 1: Core Concepts

External Resources

System V IPC Documentation

Producer-Consumer Problem

C++ and System Programming

Linux Man Pages (run in terminal)

  • man shmget, man shmat, man shmdt, man shmctl — Shared memory functions
  • man semget, man semop, man semctl — Semaphore functions
  • man 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:

  1. Part 1 - Core Concepts: IPC problem, producer-consumer pattern, three-semaphore solution, circular buffers
  2. Part 2 - Shared Memory: shmget, shmat, shmdt, shmctl — the complete lifecycle
  3. Part 3 - Semaphores: semget, semop, semctl, struct sembuf, union semun — synchronization primitives
  4. Part 4 - C Essentials: Pointers, pointer arithmetic, structs, string safety, random numbers
  5. Part 5 - Implementation: Complete consumer and producer code with signal handlers
  6. 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.