UNIX IPC STEVENS VOL2 MASTERY
Interprocess Communication is the backbone of every modern Unix system. When you run `ls | grep foo`, you're using pipes. When PostgreSQL coordinates between its processes, it uses shared memory and semaphores. When your web server handles thousands of connections, it uses message queues and synchronization primitives.
Sprint: Unix IPC Mastery - Stevens Vol 2 Complete
Goal: Master every Interprocess Communication mechanism in Unix by working through W. Richard Stevensโ โUnix Network Programming Volume 2: Interprocess Communications.โ Youโll deeply understand how processes share data and synchronizeโfrom simple pipes to complex shared memory regions, from local semaphores to remote procedure calls. By the end, youโll know exactly when to use each IPC mechanism, understand the trade-offs between POSIX and System V APIs, and have built real systems that coordinate multiple processes.
Why Unix IPC Matters
Interprocess Communication is the backbone of every modern Unix system. When you run ls | grep foo, youโre using pipes. When PostgreSQL coordinates between its processes, it uses shared memory and semaphores. When your web server handles thousands of connections, it uses message queues and synchronization primitives.
Historical Context
W. Richard Stevens wrote the definitive treatment of Unix IPC in 1999, covering mechanisms that date back to:
- 1970s: Pipes (original Unix)
- 1983: System V IPC (shared memory, semaphores, message queues)
- 1988: POSIX standardization efforts began
- 1993: POSIX.1b real-time extensions (including IPC)
Real-World Impact
According to research on IPC performance analysis:
- Unix domain sockets perform 2x faster than TCP/IP for local communication
- Shared memory remains the fastest mechanism for bulk data transfer
- Named pipes (FIFOs) are still widely used for reliability and simplicity
- Message queues provide natural request/response patterns
Modern systems like ByteDanceโs Shmipc achieve 4x throughput improvements using shared memory IPC.
The IPC Landscape
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ UNIX IPC MECHANISMS โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ DATA TRANSFER โ โ SYNCHRONIZATION โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโค โโโโโโโโโโโโโโโโโโโโโโโค โ
โ โ โ โ โ โ
โ โ โโโโโโโโโโโโโโโโโ โ โ โโโโโโโโโโโโโโโโโ โ โ
โ โ โ Pipes โ โ โ โ Mutexes & โ โ โ
โ โ โ (unnamed) โ โ โ โ Cond Vars โ โ โ
โ โ โโโโโโโโโโโโโโโโโ โ โ โโโโโโโโโโโโโโโโโ โ โ
โ โ โ โ โ โ โ โ
โ โ โผ โ โ โผ โ โ
โ โ โโโโโโโโโโโโโโโโโ โ โ โโโโโโโโโโโโโโโโโ โ โ
โ โ โ FIFOs โ โ โ โ Read-Write โ โ โ
โ โ โ (named pipes) โ โ โ โ Locks โ โ โ
โ โ โโโโโโโโโโโโโโโโโ โ โ โโโโโโโโโโโโโโโโโ โ โ
โ โ โ โ โ โ โ โ
โ โ โผ โ โ โผ โ โ
โ โ โโโโโโโโโโโโโโโโโ โ โ โโโโโโโโโโโโโโโโโ โ โ
โ โ โ Message โโโโผโโโโโผโโโ Semaphores โ โ โ
โ โ โ Queues โ โ โ โ (POSIX/SysV) โ โ โ
โ โ โ (POSIX/SysV) โ โ โ โโโโโโโโโโโโโโโโโ โ โ
โ โ โโโโโโโโโโโโโโโโโ โ โ โ โ โ
โ โ โ โ โ โผ โ โ
โ โ โผ โ โ โโโโโโโโโโโโโโโโโ โ โ
โ โ โโโโโโโโโโโโโโโโโ โ โ โ Record โ โ โ
โ โ โShared Memory โโโโผโโโโโผโโโ Locking โ โ โ
โ โ โ (POSIX/SysV) โ โ โ โ (fcntl) โ โ โ
โ โ โโโโโโโโโโโโโโโโโ โ โ โโโโโโโโโโโโโโโโโ โ โ
โ โ โ โ โ โ โ
โ โโโโโโโโโโโผโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ โ
โ โผ โ
โ โโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ REMOTE PROCEDURE โ โ
โ โ CALLS โ โ
โ โ (Sun RPC + XDR) โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ

POSIX vs System V: The Two API Families
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ POSIX vs SYSTEM V IPC โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ โ
โ POSIX IPC (Modern) System V IPC (Legacy) โ
โ โโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโ โ
โ โ
โ โ Uses filesystem namespace โ Uses numeric keys (ftok) โ
โ โ Reference counting โ Kernel-persistent โ
โ โ Simpler API โ More features (semaphore sets) โ
โ โ Better portability โ Widely deployed โ
โ โ
โ Message Queues: Message Queues: โ
โ mq_open(), mq_send() msgget(), msgsnd() โ
โ mq_receive(), mq_close() msgrcv(), msgctl() โ
โ โ
โ Semaphores: Semaphores: โ
โ sem_open(), sem_wait() semget(), semop() โ
โ sem_post(), sem_close() semctl() โ
โ โ
โ Shared Memory: Shared Memory: โ
โ shm_open(), mmap() shmget(), shmat() โ
โ shm_unlink() shmdt(), shmctl() โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ

Prerequisites & Background Knowledge
Before starting these projects, you should have foundational understanding in these areas:
Essential Prerequisites (Must Have)
Programming Skills:
- C programming (pointers, memory management, structs)
- Understanding of process creation with
fork() - Basic file I/O operations (
open,read,write,close) - Familiarity with errno and error handling
- Recommended Reading: โThe C Programming Languageโ by Kernighan & Ritchie
Operating Systems Fundamentals:
- Process vs Thread concepts
- Virtual memory basics
- File descriptors
- System calls vs library functions
- Recommended Reading: โOperating Systems: Three Easy Piecesโ by Arpaci-Dusseau โ Ch. 5-6
Unix Environment:
- Shell scripting basics
- Reading man pages (
man 2 pipe,man 7 sem_overview) - Using
straceandlsof - Recommended Reading: โAdvanced Programming in the UNIX Environmentโ by Stevens & Rago โ Ch. 1-3
Helpful But Not Required
Threading:
- pthreads basics (
pthread_create,pthread_join) - Can learn during: Projects 7-9
Networking:
- TCP/IP basics
- Can learn during: Project 18 (Sun RPC)
Self-Assessment Questions
Before starting, ask yourself:
- โ
Can you explain what happens when you call
fork()? - โ
Do you understand file descriptors and why
0,1,2are special? - โ
Can you allocate memory with
malloc()and avoid leaks? - โ
Have you used
straceto trace system calls? - โ Do you know the difference between a process and a thread?
If you answered โnoโ to questions 1-3: Spend 2 weeks on Stevensโ APUE Ch. 1-8 before starting. If you answered โyesโ to all: Youโre ready to begin!
Development Environment Setup
Required Tools:
- GCC or Clang (C11 support)
- Linux (Ubuntu 22.04+ or similar) โ macOS lacks some System V features
makefor building projectsstraceandltracefor debugging
Recommended Tools:
- Valgrind for memory leak detection
- GDB for debugging
ipcsandipcrmfor inspecting System V IPC objects
Testing Your Setup:
# Verify POSIX IPC support
$ ls /dev/shm/
# Should list any existing shared memory objects
# Verify System V IPC support
$ ipcs
------ Message Queues --------
key msqid owner perms used-bytes messages
------ Shared Memory Segments --------
key shmid owner perms bytes nattch
------ Semaphore Arrays --------
key semid owner perms nsems
# Verify compiler
$ gcc --version
gcc (Ubuntu 11.4.0-1ubuntu1~22.04) 11.4.0
# Check PIPE_BUF limit
$ getconf PIPE_BUF /
4096
Time Investment:
- Foundation projects (1-4): Weekend each (4-8 hours)
- Synchronization projects (5-10): 1 week each (10-20 hours)
- Shared Memory projects (11-14): 1-2 weeks each (15-30 hours)
- Advanced projects (15-18): 2+ weeks each (20-40 hours)
- Total sprint: 4-6 months if doing all projects sequentially
Important Reality Check:
IPC is notoriously tricky to debug. Deadlocks will happen. Processes will hang. Youโll leak semaphores and shared memory segments that persist after your program crashes. This is normal. The ipcs and ipcrm commands will become your best friends. Embrace the chaosโitโs how you learn.
Core Concept Analysis
1. Pipes: The Simplest IPC
Pipes are unidirectional byte streams between related processes. Created by pipe(), they return two file descriptors: one for reading, one for writing.
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ PIPE MECHANISM โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ โ
โ Parent Process Child Process โ
โ โโโโโโโโโโโโโโ โโโโโโโโโโโโโ โ
โ โ
โ Before fork(): โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ fd[0] โโโโโโโโโโโบ KERNEL PIPE BUFFER โโโโโโโโโโโ fd[1] โ โ
โ โ (read) (4KB - 64KB) (write) โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ
โ After fork(): โ
โ โโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโ โ
โ โ PARENT โ โ CHILD โ โ
โ โ โ โ โ โ
โ โ fd[0] (R) โ KERNEL BUFFER โ fd[0] (R) โ โ
โ โ fd[1] (W) โโโผโโโโโโโโโโโโโโโโโโโโโโบโ fd[1] (W) โ โ
โ โ โ โโโโโโโโโโโโโโโโโผโโ โ โ
โ โ close(fd[0])โ โ close(fd[1]) โ โ
โ โ write(fd[1])โ โ read(fd[0]) โ โ
โ โโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโ โ
โ โ
โ Key Insight: After fork, CLOSE the ends you don't need! โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ

Critical Constants:
PIPE_BUF: Writes up to this size are atomic (usually 4096 bytes)- If pipe is full,
write()blocks - If pipe is empty,
read()blocks - If all write ends close,
read()returns 0 (EOF) - If all read ends close,
write()generates SIGPIPE
2. FIFOs: Named Pipes for Unrelated Processes
FIFOs appear in the filesystem and allow communication between unrelated processes.
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ FIFO (Named Pipe) โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ โ
โ $ mkfifo /tmp/myfifo โ
โ $ ls -l /tmp/myfifo โ
โ prw-r--r-- 1 user user 0 Jan 1 12:00 /tmp/myfifo โ
โ ^ โ
โ โโโ 'p' indicates pipe (FIFO) โ
โ โ
โ โโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโ โ
โ โ Process A โ โ Process B โ โ
โ โ (Writer) โ โ (Reader) โ โ
โ โ โ โ โ โ
โ โ fd = open( โ โโโโโโ โ fd = open( โ โ
โ โ "/tmp/myfifo"โ โFIFOโ โ "/tmp/myfifo"โ โ
โ โ O_WRONLY) โโโโค โโโโ O_RDONLY) โ โ
โ โ โ โโโโโโ โ โ โ
โ โ write(fd, ...) โโโโโโโโโโโโ read(fd, ...) โ โ
โ โโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโ โ
โ โ
โ Blocking Behavior: โ
โ โข open() for reading BLOCKS until a writer opens โ
โ โข open() for writing BLOCKS until a reader opens โ
โ โข Use O_NONBLOCK to avoid blocking โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ

3. POSIX Message Queues
Message queues provide discrete messages with priorities, unlike the byte-stream model of pipes.
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ POSIX MESSAGE QUEUE โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ โ
โ Message Queue: /my_queue โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ Priority 31 (Highest) โ โ
โ โ โโโโโโโโโโโ โ โ
โ โ โ MSG 1 โ โ Dequeued first (highest priority) โ โ
โ โ โโโโโโโโโโโ โ โ
โ โ Priority 15 โ โ
โ โ โโโโโโโโโโโ โโโโโโโโโโโ โ โ
โ โ โ MSG 2 โ โ MSG 3 โ โ FIFO within same priority โ โ
โ โ โโโโโโโโโโโ โโโโโโโโโโโ โ โ
โ โ Priority 0 (Lowest) โ โ
โ โ โโโโโโโโโโโ โโโโโโโโโโโ โโโโโโโโโโโ โ โ
โ โ โ MSG 4 โ โ MSG 5 โ โ MSG 6 โ โ โ
โ โ โโโโโโโโโโโ โโโโโโโโโโโ โโโโโโโโโโโ โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ
โ Key Functions: โ
โ โข mq_open("/my_queue", O_CREAT | O_RDWR, 0644, &attr) โ
โ โข mq_send(mqd, buffer, len, priority) โ
โ โข mq_receive(mqd, buffer, max_len, &priority) โ
โ โข mq_notify(mqd, &sigevent) โ async notification โ
โ โข mq_getattr(mqd, &attr) / mq_setattr() โ
โ โ
โ Attributes (mq_attr): โ
โ โข mq_maxmsg โ max messages in queue โ
โ โข mq_msgsize โ max size of each message โ
โ โข mq_curmsgs โ current number of messages โ
โ โข mq_flags โ O_NONBLOCK โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ

4. System V Message Queues
Older API with message types for multiplexing multiple logical channels.
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ SYSTEM V MESSAGE QUEUE โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ โ
โ key_t key = ftok("/path/to/file", 'A'); // Generate unique key โ
โ int msqid = msgget(key, IPC_CREAT | 0644); โ
โ โ
โ Message Structure: โ
โ struct msgbuf { โ
โ long mtype; // Message type (MUST be > 0) โ
โ char mtext[N]; // Message data โ
โ }; โ
โ โ
โ Queue with Multiple Message Types: โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ mtype=1 โ mtype=2 โ mtype=1 โ mtype=3 โ mtype=2 โ โ โ
โ โ โโโโโโโ โ โโโโโโโ โ โโโโโโโ โ โโโโโโโ โ โโโโโโโ โ โ โ
โ โ โ A โ โ โ B โ โ โ C โ โ โ D โ โ โ E โ โ โ โ
โ โ โโโโโโโ โ โโโโโโโ โ โโโโโโโ โ โโโโโโโ โ โโโโโโโ โ โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ
โ Receive Modes (msgrcv msgtyp parameter): โ
โ โข msgtyp = 0 โ Receive first message (any type) โ
โ โข msgtyp > 0 โ Receive first message of type msgtyp โ
โ โข msgtyp < 0 โ Receive first message with type โค |msgtyp| โ
โ โ
โ Example: Client-Server Multiplexing โ
โ โข Server receives: msgtyp = 1 (requests) โ
โ โข Client 100 receives: msgtyp = 100 (responses for client 100) โ
โ โข Client 101 receives: msgtyp = 101 (responses for client 101) โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ

5. Mutexes and Condition Variables
The fundamental building blocks for thread synchronization.
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ MUTEX + CONDITION VARIABLE PATTERN โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ โ
โ Producer-Consumer with Bounded Buffer: โ
โ โ
โ PRODUCER CONSUMER โ
โ โโโโโโโโ โโโโโโโโ โ
โ pthread_mutex_lock(&mutex); pthread_mutex_lock(&mutex); โ
โ โ
โ while (count == MAX_SIZE) { while (count == 0) { โ
โ pthread_cond_wait( pthread_cond_wait( โ
โ ¬_full, &mutex); ¬_empty, &mutex); โ
โ } } โ
โ // CRITICAL: while, not if! // Handle spurious wakeups! โ
โ โ
โ buffer[in] = item; item = buffer[out]; โ
โ in = (in + 1) % MAX_SIZE; out = (out + 1) % MAX_SIZE; โ
โ count++; count--; โ
โ โ
โ pthread_cond_signal(¬_empty); pthread_cond_signal(¬_full); โ
โ pthread_mutex_unlock(&mutex); pthread_mutex_unlock(&mutex); โ
โ โ
โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ BOUNDED BUFFER โ โ
โ โ โโโโโโฌโโโโโฌโโโโโฌโโโโโฌโโโโโฌโโโโโฌโโโโโฌโโโโโ โ โ
โ โ โ โ โ D1 โ D2 โ D3 โ โ โ โ โ โ
โ โ โโโโโโดโโโโโดโโโโโดโโโโโดโโโโโดโโโโโดโโโโโดโโโโโ โ โ
โ โ โฒ โฒ โ โ
โ โ out in โ โ
โ โ (consumer) (producer) โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ
โ SPURIOUS WAKEUP: pthread_cond_wait() can return even when no โ
โ signal was sent. ALWAYS use while() loop, never if()! โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ

6. Read-Write Locks
Optimize for read-heavy workloads where multiple readers can proceed concurrently.
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ READ-WRITE LOCKS โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ โ
โ Concurrency Matrix: โ
โ โโโโโโโโโโโโโโฌโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโ โ
โ โ โ Reader โ Writer โ โ
โ โ โ Holding โ Holding โ โ
โ โโโโโโโโโโโโโโผโโโโโโโโโโโโโโผโโโโโโโโโโโโโโค โ
โ โ Reader โ โ โ โ โ โ
โ โ Wants โ (allowed) โ (blocked) โ โ
โ โโโโโโโโโโโโโโผโโโโโโโโโโโโโโผโโโโโโโโโโโโโโค โ
โ โ Writer โ โ โ โ โ โ
โ โ Wants โ (blocked) โ (blocked) โ โ
โ โโโโโโโโโโโโโโดโโโโโโโโโโโโโโดโโโโโโโโโโโโโโ โ
โ โ
โ Timeline Example: โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ Time: t1 t2 t3 t4 t5 t6 t7 t8 t9 โ โ
โ โ โ โ โ โ โ โ โ โ โ โ โ
โ โ R1: โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค โ โ
โ โ โ rdlock() unlock() โ โ โ
โ โ R2: โ โโโโโโโโโโโโโโโโโโโโโโโค โ โ โ
โ โ โ โ rdlock() unlock() โ โ โ โ
โ โ R3: โ โ โโโโโโโโโโโโโโโโโค โ โ โ
โ โ โ โ โ โ โ โ โ
โ โ W1: โ โ โ wrlock()โโโโโโผโโโโโBLOCKEDโโโผโโโโโโโโโโโค โ โ
โ โ โ โ โ โ โ RUNS โ โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ
โ Writer Starvation Problem: โ
โ โข If readers continuously arrive, writer may wait forever โ
โ โข Some implementations give preference to writers โ
โ โข pthread_rwlockattr_setkind_np() can set preference โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ

7. Record Locking (fcntl)
Lock byte ranges within files, enabling concurrent database access.
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ RECORD LOCKING (fcntl) โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ โ
โ File: database.db โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ Offset: 0 100 200 300 400 500 600 โ โ
โ โ โ โ โ โ โ โ โ โ โ
โ โ โโโโโโโโโดโโโโโโโโดโโโโโโโโดโโโโโโโโดโโโโโโโโดโโโโโโโโค โ โ
โ โ โ FILE DATA โ โ โ
โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ โ
โ โ โ โ
โ โ Process A: Write Lock [100-200) โ โ
โ โ โโโโโโโโโคโโโโโโโโโโโโโโโโผโโโโโโโโผโโโโโโโโผโโโโโโโโค โ โ
โ โ โฒโโโโโโโโฒ โ โ
โ โ โLOCKED โ โ โ
โ โ โ โ
โ โ Process B: Read Lock [300-500) โ โ
โ โ โโโโโโโโโผโโโโโโโโผโโโโโโโโคโโโโโโโโโโโโโโโโโโโโโโโโค โ โ
โ โ โฒโโโโโโโโโโโโโโโโฒ โ โ
โ โ โ READ LOCKED โ โ โ
โ โ โ โ
โ โ Process C wants: Write Lock [150-350) โ โ
โ โ โโโโโโโโโผโโโXXXXโโโโโโโโโผXXXXโโโโผโโโโโโโโผโโโโโโโโค โ โ
โ โ โฒCONFLICTโฒ โฒCONFLICT โ โ
โ โ BLOCKED! BLOCKED! โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ
โ struct flock { โ
โ short l_type; // F_RDLCK, F_WRLCK, F_UNLCK โ
โ short l_whence; // SEEK_SET, SEEK_CUR, SEEK_END โ
โ off_t l_start; // Starting offset โ
โ off_t l_len; // Length (0 = to EOF) โ
โ pid_t l_pid; // PID holding lock (F_GETLK only) โ
โ }; โ
โ โ
โ Commands: โ
โ โข F_SETLK โ Try to acquire lock, return error if blocked โ
โ โข F_SETLKW โ Try to acquire lock, WAIT if blocked โ
โ โข F_GETLK โ Test if lock would succeed, return blocker info โ
โ โ
โ Advisory vs Mandatory: โ
โ โข Advisory (default): Cooperative, processes can ignore locks โ
โ โข Mandatory: Kernel enforces (mount -o mand, chmod g+s,g-x) โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ

8. POSIX Semaphores
Counting semaphores for resource management and synchronization.
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ POSIX SEMAPHORES โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ โ
โ Named Semaphore (persists in filesystem): โ
โ sem_t *sem = sem_open("/my_sem", O_CREAT, 0644, initial_value); โ
โ โ
โ Unnamed Semaphore (in shared memory): โ
โ sem_t sem; โ
โ sem_init(&sem, pshared, initial_value); โ
โ // pshared = 0: thread-shared โ
โ // pshared = 1: process-shared (must be in shared memory!) โ
โ โ
โ Binary Semaphore (Mutex-like): โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ Value: 1 โ โ
โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ โ
โ โ โ sem_wait() sem_post() โ โ โ
โ โ โ โ โฒ โ โ โ
โ โ โ โผ โ โ โ โ
โ โ โ โโโโโโโโโ โโโโโโโโโ โ โ โ
โ โ โ โ 1โ0 โโโโโโโโโโโโโบโ 0โ1 โ โ โ โ
โ โ โ โ ENTER โ CRITICAL โ EXIT โ โ โ โ
โ โ โ โโโโโโโโโ SECTION โโโโโโโโโ โ โ โ
โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ
โ Counting Semaphore (Resource Pool): โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ Value: 3 (3 database connections available) โ โ
โ โ โ โ
โ โ sem_wait() โ 3โ2 (Thread A takes connection) โ โ
โ โ sem_wait() โ 2โ1 (Thread B takes connection) โ โ
โ โ sem_wait() โ 1โ0 (Thread C takes connection) โ โ
โ โ sem_wait() โ BLOCKED! (Thread D waits) โ โ
โ โ sem_post() โ 0โ1 (Thread A returns connection) โ โ
โ โ โ Thread D unblocks, 1โ0 โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ
โ Key Difference from Mutex: โ
โ โข Semaphore has no "owner" โ any thread can post โ
โ โข Can count beyond 1 โ
โ โข sem_post() from signal handler is safe โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ

9. System V Semaphores
Complex but powerful: semaphore sets with atomic multi-semaphore operations.
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ SYSTEM V SEMAPHORES โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ โ
โ Semaphore Set (3 semaphores): โ
โ key_t key = ftok("/path", 'S'); โ
โ int semid = semget(key, 3, IPC_CREAT | 0644); โ
โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ semid = 12345 โ โ
โ โ โโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโฌโโโโโโโโโโโโโโ โ โ
โ โ โ sem[0] โ sem[1] โ sem[2] โ โ โ
โ โ โ value=5 โ value=1 โ value=0 โ โ โ
โ โ โ (resources)โ (mutex) โ (counter) โ โ โ
โ โ โโโโโโโโโโโโโโโดโโโโโโโโโโโโโโดโโโโโโโโโโโโโโ โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ
โ Atomic Multi-Semaphore Operation: โ
โ struct sembuf ops[] = { โ
โ { 0, -1, 0 }, // Decrement sem[0] by 1 โ
โ { 1, -1, 0 }, // Decrement sem[1] by 1 โ
โ { 2, 1, 0 }, // Increment sem[2] by 1 โ
โ }; โ
โ semop(semid, ops, 3); // ALL THREE happen atomically! โ
โ โ
โ SEM_UNDO Flag (Crash Recovery): โ
โ struct sembuf op = { 0, -1, SEM_UNDO }; โ
โ // If process crashes, kernel automatically reverses the operation โ
โ // Kernel maintains semadj value per process per semaphore โ
โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ Process A โ โ
โ โ semop(semid, {0, -1, SEM_UNDO}, 1); // sem[0]: 5โ4 โ โ
โ โ semop(semid, {0, -1, SEM_UNDO}, 1); // sem[0]: 4โ3 โ โ
โ โ semadj[0] = +2 โ โ
โ โ โ โ
โ โ CRASH! ๐ฅ โ โ
โ โ โ โ
โ โ Kernel applies semadj: sem[0]: 3โ5 (restored!) โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ
โ Wait-for-Zero: โ
โ struct sembuf op = { 0, 0, 0 }; // Wait until sem[0] == 0 โ
โ // Useful for barrier synchronization โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ

10. POSIX Shared Memory
Map memory regions that multiple processes can access directly.
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ POSIX SHARED MEMORY โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ โ
โ Creating Shared Memory: โ
โ int fd = shm_open("/my_shm", O_CREAT | O_RDWR, 0644); โ
โ ftruncate(fd, SIZE); โ
โ void *ptr = mmap(NULL, SIZE, PROT_READ|PROT_WRITE, โ
โ MAP_SHARED, fd, 0); โ
โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ PHYSICAL MEMORY โ โ
โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ โ
โ โ โ SHARED MEMORY REGION โ โ โ
โ โ โ /dev/shm/my_shm โ โ โ
โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโฒโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ โ
โ โ โ โ โ
โ โ โโโโโโโโโโโโโโโโดโโโโโโโโโโโโโโโ โ โ
โ โ โ โ โ โ
โ โ โโโโโโโโโโโโผโโโโโโโโโโโ โโโโโโโโโโโโโโผโโโโโโโโโโโโโ โ โ
โ โ โ PROCESS A โ โ PROCESS B โ โ โ
โ โ โ Virtual Address โ โ Virtual Address โ โ โ
โ โ โ Space โ โ Space โ โ โ
โ โ โ โโโโโโโโโโโโโโ โ โ โโโโโโโโโโโโโโ โ โ โ
โ โ โ โ 0x7f... โ โ โ โ 0x7f... โ โ โ โ
โ โ โ โ mmap() โโโโโโผโโโโโโผโโโโโ mmap() โ โ โ โ
โ โ โ โ region โ โ โ โ region โ โ โ โ
โ โ โ โโโโโโโโโโโโโโ โ โ โโโโโโโโโโโโโโ โ โ โ
โ โ โโโโโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโโโโโโโโ โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ
โ MAP_SHARED vs MAP_PRIVATE: โ
โ โข MAP_SHARED: Changes visible to all processes, written to file โ
โ โข MAP_PRIVATE: Copy-on-write, changes are private โ
โ โ
โ CRITICAL: Shared memory requires EXTERNAL SYNCHRONIZATION! โ
โ Use mutexes/semaphores with PTHREAD_PROCESS_SHARED attribute โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ

11. System V Shared Memory
The older API with explicit attach/detach semantics.
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ SYSTEM V SHARED MEMORY โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ โ
โ key_t key = ftok("/path", 'M'); โ
โ int shmid = shmget(key, SIZE, IPC_CREAT | 0644); โ
โ void *ptr = shmat(shmid, NULL, 0); // Attach โ
โ // ... use shared memory ... โ
โ shmdt(ptr); // Detach โ
โ shmctl(shmid, IPC_RMID, NULL); // Mark for deletion โ
โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ shmget() creates segment in kernel โ โ
โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ โ Kernel Shared Memory Table โโ โ
โ โ โ โโโโโโโโฌโโโโโโโโฌโโโโโโโโฌโโโโโโโโฌโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ โ โshmid โ key โ size โnattch โ permissions โโโ โ
โ โ โ โโโโโโโโผโโโโโโโโผโโโโโโโโผโโโโโโโโผโโโโโโโโโโโโโโโโโโโโโโโโโโคโโ โ
โ โ โ โ 100 โ0x1234 โ 4096 โ 2 โ rw-r--r-- โโโ โ
โ โ โ โ 101 โ0x5678 โ 8192 โ 1 โ rw------- โโโ โ
โ โ โ โโโโโโโโดโโโโโโโโดโโโโโโโโดโโโโโโโโดโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ โ โ
โ โ shmat() maps to process address space โ โ
โ โ โโโโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโโโ โ โ
โ โ โ Process A โ โ Process B โ โ โ
โ โ โ nattch++ โ โ nattch++ โ โ โ
โ โ โ 0x7fff0000 โโโโโโโโผโโโโโโโผโโโถ shmid=100 โ โ โ
โ โ โโโโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโโโ โ โ
โ โ โ โ
โ โ shmctl(IPC_RMID) marks for deletion โ โ
โ โ โข Segment deleted when nattch reaches 0 โ โ
โ โ โข Survives process exit if nattch > 0 โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ
โ Attachment Address: โ
โ โข NULL: Kernel chooses address โ
โ โข Specific address: shmat(shmid, addr, SHM_RND) โ
โ โข SHM_RDONLY: Attach read-only โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ

12. Sun RPC (Remote Procedure Calls)
Make function calls across network boundaries transparently.
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ SUN RPC ARCHITECTURE โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ RPC FLOW โ โ
โ โ โ โ
โ โ CLIENT SERVER โ โ
โ โ โโโโโโ โโโโโโ โ โ
โ โ โโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโ โ โ
โ โ โ Application โ โ Application โ โ โ
โ โ โ result = add(a,b) โ int add(a,b) { โ โ โ
โ โ โโโโโโโโโโฌโโโโโโโโโ โ return a+b; โ โ โ
โ โ โ โ } โ โ โ
โ โ โผ โโโโโโโโโโฒโโโโโโโโโ โ โ
โ โ โโโโโโโโโโโโโโโโโโโ โโโโโโโโโโดโโโโโโโโโ โ โ
โ โ โ Client Stub โ โ Server Stub โ โ โ
โ โ โ (generated by โ โ (generated by โ โ โ
โ โ โ rpcgen) โ โ rpcgen) โ โ โ
โ โ โโโโโโโโโโฌโโโโโโโโโ โโโโโโโโโโฒโโโโโโโโโ โ โ
โ โ โ XDR encode โ XDR decode โ โ
โ โ โผ โ โ โ
โ โ โโโโโโโโโโโโโโโโโโโ โโโโโโโโโโดโโโโโโโโโ โ โ
โ โ โ RPC Runtime โ โ RPC Runtime โ โ โ
โ โ โโโโโโโโโโฌโโโโโโโโโ โโโโโโโโโโฒโโโโโโโโโ โ โ
โ โ โ โ โ โ
โ โ โผ โโโโโโโโโโโ NETWORK โโโโโโโโโโโโโโโโ โ โ
โ โ โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ
โ rpcgen Workflow: โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ calc.x (Interface Definition) โ โ
โ โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ โ
โ โ program CALC_PROG { โ โ
โ โ version CALC_VERS { โ โ
โ โ int ADD(operands) = 1; โ โ
โ โ int SUB(operands) = 2; โ โ
โ โ } = 1; โ โ
โ โ } = 0x20000001; โ โ
โ โ โ โ
โ โ $ rpcgen calc.x โ โ
โ โ โ โ โ
โ โ calc.h - Common definitions โ โ
โ โ calc_clnt.c - Client stub โ โ
โ โ calc_svc.c - Server main + dispatch โ โ
โ โ calc_xdr.c - XDR serialization routines โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ
โ Port Mapper (rpcbind): โ
โ โข Server registers: program number + version โ port โ
โ โข Client queries: "Where is CALC_PROG version 1?" โ
โ โข Response: "Port 45678" โ
โ โข Client connects directly to port 45678 โ
โ โ
โ XDR (External Data Representation): โ
โ โข Big-endian, 4-byte aligned โ
โ โข Handles int, float, string, arrays, structs โ
โ โข Machine-independent wire format โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ

Concept Summary Table
This section provides a map of the mental models you will build during these projects.
| Concept Cluster | What You Need to Internalize |
|---|---|
| Pipes & FIFOs | Unidirectional byte streams. Parent-child inheritance. PIPE_BUF atomicity. EOF on close. |
| Message Queues | Discrete messages vs byte streams. POSIX priorities vs System V types. Kernel persistence. |
| Mutexes & Condvars | Protect shared data, signal state changes. ALWAYS use while() not if() for conditions. |
| Read-Write Locks | Multiple readers OR one writer. Trade-off between concurrency and fairness. |
| Record Locking | Byte-range locks within files. Advisory vs mandatory. Deadlock detection. |
| Semaphores | Counting resources, not just locking. POSIX simplicity vs System V atomic sets. SEM_UNDO recovery. |
| Shared Memory | Fastest IPC but requires external synchronization. POSIX mmap vs System V attach. |
| Sun RPC | Transparently call functions across network. rpcgen for stubs, XDR for serialization. |
Deep Dive Reading by Concept
This section maps each concept to specific book chapters for deeper understanding.
Pipes & Basic IPC
| Concept | Book & Chapter | Why This Matters |
|---|---|---|
| Pipe fundamentals | โAdvanced Programming in the UNIX Environmentโ by Stevens & Rago โ Ch. 15 | Canonical explanation of pipe semantics and limitations |
| FIFO implementation | โThe Linux Programming Interfaceโ by Kerrisk โ Ch. 44 | Modern Linux-specific details and edge cases |
| Shell pipelines | โThe Linux Command Lineโ by Shotts โ Ch. 6 | Understanding how the shell uses pipes |
Message Queues
| Concept | Book & Chapter | Why This Matters |
|---|---|---|
| POSIX message queues | โThe Linux Programming Interfaceโ by Kerrisk โ Ch. 52 | Complete POSIX MQ coverage with examples |
| System V message queues | โAdvanced Programming in the UNIX Environmentโ by Stevens & Rago โ Ch. 15.7 | Classic treatment with design rationale |
Synchronization Primitives
| Concept | Book & Chapter | Why This Matters |
|---|---|---|
| Mutexes & Condvars | โProgramming with POSIX Threadsโ by Butenhof โ Ch. 3 | The definitive pthreads reference |
| Deadlock avoidance | โOperating Systems: Three Easy Piecesโ by Arpaci-Dusseau โ Ch. 32 | Theoretical foundation for deadlock |
| Semaphore theory | โOperating Systems: Three Easy Piecesโ by Arpaci-Dusseau โ Ch. 31 | Dijkstraโs semaphores explained clearly |
Shared Memory
| Concept | Book & Chapter | Why This Matters |
|---|---|---|
| mmap fundamentals | โThe Linux Programming Interfaceโ by Kerrisk โ Ch. 49 | Memory mapping in depth |
| POSIX shared memory | โThe Linux Programming Interfaceโ by Kerrisk โ Ch. 54 | Modern shared memory API |
| System V shared memory | โAdvanced Programming in the UNIX Environmentโ by Stevens & Rago โ Ch. 15.9 | Classic shm* API |
Remote Procedure Calls
| Concept | Book & Chapter | Why This Matters |
|---|---|---|
| RPC concepts | โDistributed Systemsโ by Tanenbaum โ Ch. 4 | Theoretical RPC foundation |
| XDR serialization | โUnix Network Programming Vol 2โ by Stevens โ Ch. 16 | Wire format details |
Quick Start: Your First 48 Hours
Feeling overwhelmed? Start here instead of reading everything:
Day 1 (4 hours):
- Read only the โPipesโ section in Core Concepts above
- Open a terminal and run:
echo "hello" | catโ you just used a pipe! - Start Project 1 - write a simple program that creates a pipe and forks
- Donโt worry about FIFOs or message queues yet
Day 2 (4 hours):
- Add error handling to your Project 1 code
- Experiment with closing/not closing pipe ends
- See what happens when you write more than PIPE_BUF bytes
- Read โThe Core Question Youโre Answeringโ for Project 1
End of Weekend:
You now understand that pipes are kernel-buffered byte streams connecting related processes. You can explain โwhy does ls | grep foo work?โ to someone. Thatโs 80% of the pipe mental model.
Next Steps:
- If it clicked: Continue to Project 2 (FIFOs)
- If confused: Re-read man pages (
man 2 pipe,man 7 pipe) - If frustrated: Take a break! IPC is hard. Read Kerrisk Ch. 44 slowly.
Recommended Learning Path
Path 1: The Systems Programmer (Recommended Start)
Best for: Those who want a solid foundation in all IPC mechanisms
- Start with Projects 1-3 (Pipes, FIFOs, popen) - Build intuition for byte streams
- Then Projects 4-6 (Message Queues) - Understand discrete messages
- Then Projects 7-9 (Synchronization) - Master locking primitives
- Then Projects 10-14 (Shared Memory) - The fastest IPC
- Finally Projects 15-18 (RPC) - Cross-network IPC
Path 2: The Performance Engineer
Best for: Those optimizing high-throughput systems
- Start with Projects 10-12 (Shared Memory) - Skip to the fastest mechanism
- Then Projects 7-9 (Semaphores) - Synchronize shared memory
- Then Project 13 (Lock-free) - Eliminate synchronization overhead
- Back to Projects 1-6 (Pipes/MQ) - Understand simpler alternatives
Path 3: The Completionist
Best for: Those building a complete IPC reference implementation
Phase 1: Data Transfer (Weeks 1-3)
- Projects 1-6 (All pipe and message queue variants)
Phase 2: Synchronization (Weeks 4-6)
- Projects 7-10 (All locking mechanisms)
Phase 3: Shared Memory (Weeks 7-9)
- Projects 11-14 (POSIX and System V)
Phase 4: Remote IPC (Weeks 10-12)
- Projects 15-18 (Sun RPC complete)
Project List
The following projects guide you from basic pipes to complete RPC systems, covering every IPC mechanism from Stevensโ book.
Project 1: Build a Shell Pipeline Executor
- File: P01-shell-pipeline.md
- Main Programming Language: C
- Alternative Programming Languages: Rust, Go
- Coolness Level: Level 3 (Genuinely Clever)
- Business Potential: Level 1 (Resume Gold)
- Difficulty: Level 2 (Intermediate)
- Knowledge Area: Operating Systems, Process Management
- Software or Tool: None (pure syscalls)
- Main Book: โAdvanced Programming in the UNIX Environmentโ by Stevens & Rago
What youโll build: A program that executes cmd1 | cmd2 | cmd3 by creating pipes and forking processes, just like a real shell.
Why it teaches IPC: Forces you to understand pipe file descriptor inheritance across fork(), the critical importance of closing unused ends, and how EOF propagates through a pipeline.
Core challenges youโll face:
- File descriptor management โ Understanding dup2() and close()
- Process coordination โ Multiple fork() calls, parent waiting for children
- EOF handling โ Why closing write ends matters
Real World Outcome
When complete, youโll have a program that can execute arbitrary shell pipelines:
What you will see:
- Working pipeline execution: Your program runs
ls -la | grep txt | wc -l - Correct output: Same result as typing it in bash
- Proper cleanup: No zombie processes, no leaked file descriptors
Command Line Outcome Example:
# 1. Compile your pipeline executor
$ gcc -o mypipe mypipe.c
# No errors
# 2. Run a simple two-command pipeline
$ ./mypipe "ls -la" "grep txt"
-rw-r--r-- 1 user user 1234 Jan 1 12:00 notes.txt
-rw-r--r-- 1 user user 5678 Jan 1 12:00 data.txt
# 3. TEST: Three-command pipeline
$ ./mypipe "cat /etc/passwd" "grep root" "cut -d: -f1"
root
# Output shows only "root" - data flowed through all three commands
# 4. TEST: Verify no file descriptor leaks
$ ./mypipe "ls" "cat" &
$ ls -la /proc/$!/fd
total 0
lrwx------ 1 user user 64 Jan 1 12:00 0 -> /dev/pts/0
lrwx------ 1 user user 64 Jan 1 12:00 1 -> /dev/pts/0
lrwx------ 1 user user 64 Jan 1 12:00 2 -> /dev/pts/0
# Only 0,1,2 - no leaked pipe fds!
# 5. TEST: Verify no zombie processes
$ ./mypipe "sleep 1" "cat"
$ ps aux | grep defunct
# No output - no zombies
The Core Question Youโre Answering
โHow does
ls | grep foo | wc -lactually work at the system call level?โ
Before you write any code, trace this on paper: the shell calls pipe() twice, fork() three times, and uses dup2() to rewire stdin/stdout. Each process inherits all file descriptors, but must close the ones it doesnโt use. If you donโt close the write end of a pipe, the reader never gets EOF.
Concepts You Must Understand First
Stop and research these before coding:
- File Descriptor Inheritance
- What file descriptors does a child inherit after fork()?
- Why does closing fd in parent not affect child?
- Book Reference: โAPUEโ Ch. 3 - Stevens & Rago
- dup2() System Call
- How does dup2(oldfd, newfd) work?
- What happens to newfd if it was already open?
- Book Reference: โAPUEโ Ch. 3.12 - Stevens & Rago
- Pipe Capacity and Blocking
- What is PIPE_BUF on your system?
- When does write() to a pipe block?
- Book Reference: โThe Linux Programming Interfaceโ Ch. 44 - Kerrisk
Questions to Guide Your Design
Before implementing, think through these:
- Pipe Creation Order
- Create all pipes before any fork, or one at a time?
- How do you track which pipe connects which processes?
- File Descriptor Cleanup
- Which process closes which ends of which pipes?
- What happens if you forget to close the write end?
- Error Handling
- What if exec() fails?
- What if pipe() fails partway through?
Thinking Exercise
Trace the File Descriptors
Before coding, draw the file descriptor table for this pipeline: A | B | C
After pipe(p1), pipe(p2), before any fork:
Parent FD table:
3 โ p1[0] (read end of pipe 1)
4 โ p1[1] (write end of pipe 1)
5 โ p2[0] (read end of pipe 2)
6 โ p2[1] (write end of pipe 2)
After forking child A:
Child A needs: stdin=p1[0]? No wait... A writes TO pipe.
Actually, A's stdout should go to p1[1].
[Continue this trace for all processes...]
Questions while tracing:
- Which fds does process A close after dup2?
- What would happen if B doesnโt close p1[1]?
- When does Aโs write() return EOF?
The Interview Questions Theyโll Ask
Prepare to answer these:
- โWalk me through what happens when you type
ls | grep fooin a shell.โ - โWhy is it important to close unused pipe file descriptors?โ
- โWhatโs the difference between pipe() and pipe2() on Linux?โ
- โHow would you implement a pipeline with error propagation (pipefail)?โ
- โWhat happens if a process in the middle of a pipeline dies?โ
Hints in Layers
Hint 1: Start Simple First, make a TWO command pipeline work. Ignore three+ commands until two works perfectly.
Hint 2: The Pattern
For A | B: Create one pipe. Fork twice. Child A: dup2(pipe_write, STDOUT), close both ends, exec. Child B: dup2(pipe_read, STDIN), close both ends, exec. Parent: close both ends, wait for both children.
Hint 3: Generalizing
// Pseudocode for N commands
create N-1 pipes
for i in 0..N:
fork child i
in child:
if i > 0:
dup2(pipe[i-1].read, STDIN)
if i < N-1:
dup2(pipe[i].write, STDOUT)
close ALL pipe fds (very important!)
exec(command[i])
parent: close all pipe fds, wait for all children
Hint 4: Debugging
Use strace -f ./mypipe "ls" "cat" to see all syscalls across all processes. Look for unclosed fds or blocking reads.
Books That Will Help
| Topic | Book | Chapter |
|---|---|---|
| Pipe semantics | โAdvanced Programming in the UNIX Environmentโ by Stevens & Rago | Ch. 15.2 |
| Process creation | โOperating Systems: Three Easy Piecesโ by Arpaci-Dusseau | Ch. 5 |
| File descriptors | โThe Linux Programming Interfaceโ by Kerrisk | Ch. 5 |
Common Pitfalls & Debugging
Problem 1: โPipeline hangs foreverโ
- Why: The read end is waiting for EOF, but some process still has the write end open.
- Fix: Make sure EVERY process closes EVERY pipe fd it doesnโt need, BEFORE exec.
- Quick test:
ls -la /proc/$(pgrep yourprogram)/fd- should only show 0,1,2
Problem 2: โBroken pipe errorโ
- Why: Writer tried to write after reader closed its end.
- Fix: Usually means reader died unexpectedly. Check your exec() error handling.
- Debug: Add signal handler for SIGPIPE to see where it happens.
Problem 3: โZombie processes remainโ
- Why: Parent isnโt calling wait() or waitpid() for each child.
- Fix: Parent must wait for ALL children after closing its pipe fds.
- Quick test:
ps aux | grep defunctshould show nothing
Testing Your Pipeline:
# Compare your output to bash
$ ./mypipe "ls" "wc -l"
$ ls | wc -l
# Should be identical
# Test with large data
$ ./mypipe "seq 1 1000000" "wc -l"
1000000
# Should not deadlock
Project 2: Client-Server with Named Pipes (FIFOs)
- File: P02-fifo-client-server.md
- Main Programming Language: C
- Alternative Programming Languages: Rust, Python
- Coolness Level: Level 3 (Genuinely Clever)
- Business Potential: Level 2 (Micro-SaaS potential)
- Difficulty: Level 2 (Intermediate)
- Knowledge Area: IPC, Client-Server Architecture
- Software or Tool: None (pure syscalls)
- Main Book: โThe Linux Programming Interfaceโ by Michael Kerrisk
What youโll build: A simple calculator server that listens on a well-known FIFO, receives requests from multiple clients, and sends responses back through client-specific FIFOs.
Why it teaches IPC: FIFOs allow unrelated processes to communicate. This project teaches the common pattern of one well-known server FIFO plus per-client response FIFOs.
Core challenges youโll face:
- Blocking behavior โ open() blocks until both ends connected
- Atomic writes โ Keeping requests from multiple clients separate
- Cleanup โ Removing FIFOs on shutdown
Real World Outcome
What you will see:
- Server running: Waiting for connections on
/tmp/calc_server - Multiple clients: Each creates its own response FIFO
- Concurrent requests: Clients donโt interfere with each other
Command Line Outcome Example:
# Terminal 1: Start server
$ ./calc_server
Server listening on /tmp/calc_server...
Received: 5 + 3 from client 12345
Sending response: 8 to /tmp/calc_client_12345
Received: 10 * 4 from client 12346
Sending response: 40 to /tmp/calc_client_12346
# Terminal 2: Client 1
$ ./calc_client "5 + 3"
Result: 8
# Terminal 3: Client 2 (simultaneously)
$ ./calc_client "10 * 4"
Result: 40
# Verify FIFOs exist
$ ls -la /tmp/calc*
prw-r--r-- 1 user user 0 Jan 1 12:00 /tmp/calc_server
prw-r--r-- 1 user user 0 Jan 1 12:00 /tmp/calc_client_12345
prw-r--r-- 1 user user 0 Jan 1 12:00 /tmp/calc_client_12346
# After clients exit, their FIFOs are cleaned up
$ ls -la /tmp/calc_client*
ls: cannot access '/tmp/calc_client*': No such file or directory
The Core Question Youโre Answering
โHow can unrelated processes communicate when they canโt share file descriptors through fork()?โ
Named pipes (FIFOs) live in the filesystem, so any process can open them by path. The challenge is the bidirectional communication pattern: server needs to know where to send the response. The standard solution is client-specific response FIFOs.
Concepts You Must Understand First
Stop and research these before coding:
- FIFO Creation and Permissions
- What does mkfifo() do? Whatโs the mode parameter?
- What happens if FIFO already exists?
- Book Reference: โThe Linux Programming Interfaceโ Ch. 44.7 - Kerrisk
- Blocking Semantics
- When does open() on a FIFO block?
- Whatโs O_NONBLOCK behavior for FIFOs?
- Book Reference: โAPUEโ Ch. 15.5 - Stevens & Rago
- Atomic Writes
- What happens if two clients write simultaneously?
- Whatโs PIPE_BUF and why does it matter for FIFOs?
Hints in Layers
Hint 1: The Protocol
Client sends: "pid:operation\n" (e.g., โ12345:5 + 3\nโ). Server extracts pid, computes, responds to /tmp/calc_client_<pid>.
Hint 2: FIFO Lifecycle Server: mkfifo(โ/tmp/calc_serverโ), open(O_RDONLY), read loop. Client: mkfifo(โ/tmp/calc_client_PIDโ), open server FIFO O_WRONLY, write request, open client FIFO O_RDONLY, read response, unlink client FIFO.
Hint 3: Handling EOF When all writers close, read() returns 0. Server should re-open or use O_RDWR trick to prevent EOF.
Books That Will Help
| Topic | Book | Chapter |
|---|---|---|
| FIFO details | โThe Linux Programming Interfaceโ by Kerrisk | Ch. 44 |
| Client-server patterns | โUnix Network Programming Vol 2โ by Stevens | Ch. 4 |
| Atomic I/O | โAPUEโ by Stevens & Rago | Ch. 3.11 |
Project 3: Implement popen() and pclose()
- File: P03-implement-popen.md
- Main Programming Language: C
- Coolness Level: Level 4 (Hardcore Tech Flex)
- Business Potential: Level 1 (Resume Gold)
- Difficulty: Level 3 (Advanced)
- Knowledge Area: Library Implementation, Process Management
What youโll build: Your own implementation of the popen() and pclose() library functions.
Why it teaches IPC: popen() combines pipe(), fork(), dup2(), and exec() into a convenient API. Building it yourself ensures you understand all the pieces.
Core challenges youโll face:
- Bidirectional limitation โ Why popen is read OR write, not both
- Process tracking โ Mapping FILE* to child pid for pclose()
- Signal handling โ What happens if child ignores SIGPIPE?
Real World Outcome
# Your implementation passes the same tests as the real popen
$ ./test_mypopen
Testing mypopen("ls", "r")...
Read: file1.txt\nfile2.txt\n
mypclose returned: 0
PASS
Testing mypopen("cat", "w")...
Wrote: "Hello from parent\n"
mypclose returned: 0
PASS
Testing mypopen("exit 42", "r")...
mypclose returned: 42
PASS (exit status preserved)
The Core Question Youโre Answering
โWhy does popen() only support reading OR writing, not both?โ
Think about it: with a single pipe, data flows one direction. For bidirectional communication, youโd need two pipes and careful coordination to avoid deadlock. popen() is a convenience function for simple cases.
Project 4: POSIX Message Queue Priority Dispatcher
- File: P04-posix-mq-dispatcher.md
- Main Programming Language: C
- Alternative Programming Languages: Rust
- Coolness Level: Level 3 (Genuinely Clever)
- Business Potential: Level 3 (Service & Support Model)
- Difficulty: Level 3 (Advanced)
- Knowledge Area: IPC, Task Scheduling
What youโll build: A job dispatcher that uses POSIX message queue priorities to ensure high-priority jobs are processed first.
Why it teaches IPC: Unlike pipes, message queues preserve message boundaries and support priorities. This project makes those features essential.
Core challenges youโll face:
- Priority inversion โ Low-priority jobs starving
- Queue full handling โ What to do when mq_send blocks
- Async notification โ Using mq_notify for efficiency
Real World Outcome
# Start the dispatcher
$ ./job_dispatcher &
Dispatcher running, queue: /job_queue
# Submit jobs with different priorities
$ ./submit_job --priority=0 --cmd="sleep 10" # Low priority
Job 1 queued (priority 0)
$ ./submit_job --priority=31 --cmd="echo URGENT" # High priority
Job 2 queued (priority 31)
$ ./submit_job --priority=15 --cmd="date" # Medium priority
Job 3 queued (priority 15)
# Watch dispatcher output - processes high priority first!
Dispatcher: Processing job 2 (priority 31): echo URGENT
URGENT
Dispatcher: Processing job 3 (priority 15): date
Sat Jan 1 12:00:00 UTC 2025
Dispatcher: Processing job 1 (priority 0): sleep 10
(10 seconds later...)
Concepts You Must Understand First
- mq_attr Structure
- mq_maxmsg: How many messages can queue hold?
- mq_msgsize: Maximum size of each message?
- Book Reference: โThe Linux Programming Interfaceโ Ch. 52 - Kerrisk
- Priority Ordering
- Higher priority = dequeued first
- FIFO within same priority level
- mq_notify for Async
- Notify via signal or thread when message arrives
- One-shot: must re-register after each notification
Project 5: System V Message Queue Multi-Client Server
- File: P05-sysv-mq-server.md
- Main Programming Language: C
- Coolness Level: Level 3 (Genuinely Clever)
- Business Potential: Level 2 (Micro-SaaS)
- Difficulty: Level 3 (Advanced)
- Knowledge Area: IPC, Multiplexing
What youโll build: A server using System V message queues where clients multiplex on a single queue using message types.
Why it teaches IPC: System V message types allow selective receives (msgtyp parameter), enabling multiple logical channels on one queue.
Core challenges youโll face:
- ftok collisions โ Understanding key generation
- Message type protocol โ Designing the multiplexing scheme
- Cleanup on crash โ Orphaned queues persist!
Real World Outcome
# Server uses one queue for all clients
$ ./sysv_server
Server starting, key=0x12345678, msqid=100
Waiting for requests (msgtyp=1)...
# Client 1 (PID 5001) sends request
$ ./sysv_client "HELLO"
Sending to server (msgtyp=1)
Waiting for response (msgtyp=5001)
Response: HELLO_PROCESSED
# Client 2 (PID 5002) sends simultaneously
$ ./sysv_client "WORLD"
Response: WORLD_PROCESSED
# Both used the SAME queue, but different message types!
$ ipcs -q
------ Message Queues --------
key msqid owner perms used-bytes messages
0x12345678 100 user 644 0 0
The Core Question Youโre Answering
โHow can multiple clients share a single message queue without mixing up their responses?โ
Message types are the key. Server receives msgtyp=1 (requests). Each client sends its PID in the request, and server responds with msgtyp=
Project 6: Message Queue Performance Benchmark
- File: P06-mq-benchmark.md
- Main Programming Language: C
- Coolness Level: Level 2 (Practical but Forgettable)
- Business Potential: Level 1 (Resume Gold)
- Difficulty: Level 2 (Intermediate)
- Knowledge Area: Performance Analysis, IPC
What youโll build: A benchmark comparing POSIX vs System V message queues, pipes, and Unix domain sockets.
Why it teaches IPC: Numbers donโt lie. Youโll see exactly when each mechanism shines.
Real World Outcome
$ ./ipc_benchmark --msg-size=1024 --iterations=100000
IPC Mechanism Benchmark (1KB messages, 100K iterations)
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Mechanism Throughput Latency (avg) Latency (p99)
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Pipe 1.2 GB/s 0.8 ฮผs 2.1 ฮผs
FIFO 1.1 GB/s 0.9 ฮผs 2.4 ฮผs
Unix Domain Socket 1.4 GB/s 0.7 ฮผs 1.8 ฮผs
POSIX Message Queue 0.8 GB/s 1.2 ฮผs 3.5 ฮผs
System V Message Queue 0.6 GB/s 1.6 ฮผs 4.2 ฮผs
Shared Memory + Sem 2.8 GB/s 0.3 ฮผs 0.8 ฮผs
Winner: Shared Memory (for raw throughput)
Best simplicity: Unix Domain Socket
Project 7: Producer-Consumer with Mutexes and Condition Variables
- File: P07-producer-consumer.md
- Main Programming Language: C
- Alternative Programming Languages: Rust, C++
- Coolness Level: Level 3 (Genuinely Clever)
- Business Potential: Level 1 (Resume Gold)
- Difficulty: Level 3 (Advanced)
- Knowledge Area: Concurrency, Synchronization
What youโll build: The classic bounded-buffer producer-consumer with multiple producers and consumers using pthreads.
Why it teaches IPC: This is THE canonical synchronization problem. If you can implement this correctly, you understand mutexes and condition variables.
Core challenges youโll face:
- Spurious wakeups โ Why while() not if()
- Broadcast vs Signal โ When to use each
- Deadlock potential โ Lock ordering matters
Real World Outcome
$ ./producer_consumer --producers=3 --consumers=2 --buffer-size=10 --items=1000
Producer-Consumer Simulation
Buffer size: 10, Producers: 3, Consumers: 2, Items: 1000
[P1] Produced item 1
[P2] Produced item 2
[C1] Consumed item 1
[P3] Produced item 3
[P1] Produced item 4
[C2] Consumed item 2
...
[C1] Consumed item 1000
Statistics:
Total produced: 1000
Total consumed: 1000
Buffer empty waits: 234
Buffer full waits: 156
PASS: All items produced and consumed correctly
The Core Question Youโre Answering
โWhy must you ALWAYS use while() instead of if() when checking conditions after pthread_cond_wait()?โ
Spurious wakeups. The POSIX specification explicitly allows pthread_cond_wait() to return even when no signal was sent. Production code that uses if instead of while will eventually fail.
Project 8: Reader-Writer Lock Implementation
- File: P08-rwlock-impl.md
- Main Programming Language: C
- Coolness Level: Level 4 (Hardcore Tech Flex)
- Business Potential: Level 1 (Resume Gold)
- Difficulty: Level 4 (Expert)
- Knowledge Area: Concurrency, Lock Design
What youโll build: Your own read-write lock implementation using only mutexes and condition variables.
Why it teaches IPC: Implementing rwlock from primitives reveals the design decisions: reader preference vs writer preference, and the starvation trade-offs.
Core challenges youโll face:
- Writer starvation โ Continuous readers block writers forever
- Reader starvation โ Giving writers priority starves readers
- Fair queuing โ FIFO ordering is complex
Real World Outcome
$ ./test_myrwlock --readers=10 --writers=3 --duration=10s
My RWLock Test (10 readers, 3 writers, 10 seconds)
Reader 1 acquired read lock (0 writers waiting)
Reader 2 acquired read lock (0 writers waiting)
Reader 3 acquired read lock (0 writers waiting)
Writer 1 waiting... (3 readers active)
Reader 1 released read lock
Reader 2 released read lock
Reader 3 released read lock
Writer 1 acquired write lock
Writer 1 released write lock
Reader 4 acquired read lock
...
Statistics:
Read operations: 15,234
Write operations: 456
Max reader wait: 12ms
Max writer wait: 45ms
No starvation detected: PASS
Project 9: Record Locking Database
- File: P09-record-locking-db.md
- Main Programming Language: C
- Coolness Level: Level 3 (Genuinely Clever)
- Business Potential: Level 2 (Micro-SaaS)
- Difficulty: Level 3 (Advanced)
- Knowledge Area: File Systems, Databases
What youโll build: A simple key-value store where concurrent processes can read and write records using fcntl byte-range locks.
Why it teaches IPC: Record locking is how databases allow concurrent access. Youโll implement fine-grained locking on a shared file.
Core challenges youโll face:
- Deadlock detection โ fcntl with F_SETLKW can deadlock
- Lock inheritance โ What happens across fork()?
- Advisory nature โ Uncooperative processes can ignore locks
Real World Outcome
# Start multiple concurrent clients
$ ./kvstore set key1 value1 &
$ ./kvstore set key2 value2 &
$ ./kvstore get key1 &
$ ./kvstore set key1 updated &
# All complete without corruption
$ ./kvstore get key1
updated
$ ./kvstore get key2
value2
# Verify locking works
$ ./kvstore lock-test key1
Process 1: Acquired write lock on key1
Process 2: Waiting for lock...
(Process 1 holds lock for 2 seconds)
Process 1: Released lock
Process 2: Acquired write lock on key1
Project 10: POSIX Semaphore Connection Pool
- File: P10-semaphore-pool.md
- Main Programming Language: C
- Coolness Level: Level 3 (Genuinely Clever)
- Business Potential: Level 3 (Service & Support)
- Difficulty: Level 3 (Advanced)
- Knowledge Area: Resource Management, Concurrency
What youโll build: A database connection pool manager using POSIX counting semaphores to limit concurrent connections.
Why it teaches IPC: Counting semaphores are perfect for resource pools. The semaphore value represents available resources.
Core challenges youโll face:
- sem_timedwait โ Handling connection timeout
- Process-shared semaphores โ Allocating in shared memory
- Cleanup on crash โ Returning connections when process dies
Real World Outcome
$ ./pool_manager --max-connections=5 &
Pool manager started (5 connections available)
# Spawn 10 workers, each needs a connection
$ for i in {1..10}; do ./worker $i & done
Worker 1: Acquired connection (4 remaining)
Worker 2: Acquired connection (3 remaining)
Worker 3: Acquired connection (2 remaining)
Worker 4: Acquired connection (1 remaining)
Worker 5: Acquired connection (0 remaining)
Worker 6: Waiting for connection...
Worker 7: Waiting for connection...
Worker 1: Released connection (1 remaining)
Worker 6: Acquired connection (0 remaining)
...
All workers completed successfully
Project 11: System V Semaphore Barrier
- File: P11-sysv-barrier.md
- Main Programming Language: C
- Coolness Level: Level 3 (Genuinely Clever)
- Business Potential: Level 1 (Resume Gold)
- Difficulty: Level 3 (Advanced)
- Knowledge Area: Parallel Computing, Synchronization
What youโll build: A barrier synchronization primitive using System V semaphores where N processes wait until all arrive.
Why it teaches IPC: System V semaphores can atomically operate on multiple semaphores, enabling complex synchronization patterns.
Core challenges youโll face:
- Atomic multi-semaphore ops โ Using semop with multiple sembuf
- SEM_UNDO โ Handling process crashes mid-barrier
- Reusable barrier โ Resetting for the next round
Real World Outcome
$ ./barrier_test --processes=5 --iterations=3
Barrier Test (5 processes, 3 iterations)
[P1] Reached barrier 1
[P3] Reached barrier 1
[P2] Reached barrier 1
[P5] Reached barrier 1
[P4] Reached barrier 1
--- All processes passed barrier 1 ---
[P2] Reached barrier 2
[P1] Reached barrier 2
[P4] Reached barrier 2
[P3] Reached barrier 2
[P5] Reached barrier 2
--- All processes passed barrier 2 ---
[P1] Reached barrier 3
[P5] Reached barrier 3
[P3] Reached barrier 3
[P4] Reached barrier 3
[P2] Reached barrier 3
--- All processes passed barrier 3 ---
All processes synchronized correctly. PASS
Project 12: POSIX Shared Memory Ring Buffer
- File: P12-shm-ringbuffer.md
- Main Programming Language: C
- Alternative Programming Languages: Rust
- Coolness Level: Level 4 (Hardcore Tech Flex)
- Business Potential: Level 4 (Open Core Infrastructure)
- Difficulty: Level 4 (Expert)
- Knowledge Area: High-Performance IPC, Lock-Free
What youโll build: A high-performance ring buffer in POSIX shared memory for producer-consumer communication between processes.
Why it teaches IPC: Shared memory is the fastest IPC, but requires careful synchronization. A ring buffer with proper memory barriers is a production pattern.
Core challenges youโll face:
- Memory mapping โ shm_open, ftruncate, mmap
- Synchronization โ Semaphores in shared memory
- Cache coherency โ Memory barriers and atomics
Real World Outcome
$ ./shm_ringbuffer_bench --buffer-size=1MB --iterations=10M
Shared Memory Ring Buffer Benchmark
Buffer: 1MB, Messages: 10 million
Producer: Writing 10M messages...
Consumer: Reading 10M messages...
Results:
Throughput: 8.5 million msg/sec
Bandwidth: 8.5 GB/sec
Latency (avg): 0.12 ฮผs
Latency (p99): 0.35 ฮผs
Zero-copy: Yes
Comparison to pipe: 7x faster
Comparison to Unix socket: 6x faster
Project 13: System V Shared Memory Image Processor
- File: P13-sysv-shm-images.md
- Main Programming Language: C
- Coolness Level: Level 3 (Genuinely Clever)
- Business Potential: Level 2 (Micro-SaaS)
- Difficulty: Level 3 (Advanced)
- Knowledge Area: Image Processing, Parallel Computing
What youโll build: An image processing pipeline where multiple worker processes share a large image in System V shared memory.
Why it teaches IPC: Large data (images, matrices) should never be copied through pipes. Shared memory lets workers operate on the same data.
Core challenges youโll face:
- Segment sizing โ shmget with large sizes
- Work partitioning โ Each worker processes a region
- Coordination โ Signaling when all workers are done
Real World Outcome
$ ./shm_image_filter input.png output.png --filter=blur --workers=4
Shared Memory Image Processor
Image: 4096x4096 (64MB)
Workers: 4
Filter: Gaussian blur
[Master] Loaded image into shared memory (shmid=54321)
[Worker 1] Processing rows 0-1023
[Worker 2] Processing rows 1024-2047
[Worker 3] Processing rows 2048-3071
[Worker 4] Processing rows 3072-4095
[Worker 2] Done (245ms)
[Worker 1] Done (251ms)
[Worker 4] Done (248ms)
[Worker 3] Done (250ms)
[Master] All workers complete, writing output
Total time: 255ms (vs 980ms single-threaded)
Speedup: 3.84x with 4 workers
Project 14: Memory-Mapped File Database
- File: P14-mmap-database.md
- Main Programming Language: C
- Coolness Level: Level 4 (Hardcore Tech Flex)
- Business Potential: Level 3 (Service & Support)
- Difficulty: Level 4 (Expert)
- Knowledge Area: Databases, File Systems
What youโll build: A simple embedded database using mmap() for zero-copy persistence.
Why it teaches IPC: mmap() is shared memory backed by a file. Changes persist automatically. This is how SQLite, LMDB, and other embedded databases work.
Core challenges youโll face:
- File growth โ Remapping when database grows
- Crash consistency โ msync and fsync
- Concurrent access โ Record locking + mmap
Real World Outcome
$ ./mmapdb create mydata.db --size=100MB
Database created: mydata.db (100MB)
$ ./mmapdb insert mydata.db user:1 '{"name":"Alice"}'
Inserted at offset 0
$ ./mmapdb insert mydata.db user:2 '{"name":"Bob"}'
Inserted at offset 256
$ ./mmapdb get mydata.db user:1
{"name":"Alice"}
# Kill the process abruptly
$ ./mmapdb insert mydata.db user:3 '{"name":"Charlie"}' &
$ kill -9 $!
# Data still persists (mmap wrote to file)
$ ./mmapdb get mydata.db user:3
{"name":"Charlie"}
Project 15: Lock-Free SPSC Queue
- File: P15-lockfree-spsc.md
- Main Programming Language: C
- Alternative Programming Languages: Rust, C++
- Coolness Level: Level 5 (Pure Magic)
- Business Potential: Level 4 (Open Core)
- Difficulty: Level 5 (Master)
- Knowledge Area: Lock-Free Programming, Memory Models
What youโll build: A single-producer single-consumer lock-free queue in shared memory using only atomic operations.
Why it teaches IPC: Lock-free programming eliminates mutex overhead but requires understanding memory ordering. This is how high-frequency trading systems work.
Core challenges youโll face:
- Memory barriers โ __atomic_thread_fence, seq_cst vs relaxed
- False sharing โ Cache line padding
- ABA problem โ Sequence numbers
Real World Outcome
$ ./lockfree_bench --iterations=100M
Lock-Free SPSC Queue Benchmark
Operations: 100 million
Lock-free queue: 45M ops/sec
Mutex-based queue: 8M ops/sec
Semaphore-based: 6M ops/sec
Speedup: 5.6x over mutex
Latency (p99): 22ns vs 180ns
Project 16: Basic RPC Calculator
- File: P16-rpc-calculator.md
- Main Programming Language: C
- Coolness Level: Level 3 (Genuinely Clever)
- Business Potential: Level 2 (Micro-SaaS)
- Difficulty: Level 3 (Advanced)
- Knowledge Area: Distributed Systems, RPC
What youโll build: A calculator service using Sun RPC with rpcgen-generated stubs.
Why it teaches IPC: RPC extends IPC across network boundaries. Youโll see how rpcgen generates client stubs, server skeletons, and XDR serialization.
Core challenges youโll face:
- rpcgen syntax โ Writing .x interface files
- XDR types โ Serializing complex structures
- portmapper โ Registering and looking up services
Real World Outcome
# Define the interface (calc.x)
$ cat calc.x
program CALC_PROG {
version CALC_VERS {
int ADD(operands) = 1;
int SUBTRACT(operands) = 2;
int MULTIPLY(operands) = 3;
} = 1;
} = 0x31230000;
# Generate stubs
$ rpcgen calc.x
# Creates: calc.h, calc_clnt.c, calc_svc.c, calc_xdr.c
# Start server
$ ./calc_server &
Registered with portmapper: program 0x31230000, version 1
# Run client
$ ./calc_client localhost add 5 3
Result: 8
$ ./calc_client localhost multiply 7 6
Result: 42
# Query portmapper
$ rpcinfo -p localhost | grep 31230000
100000 4 tcp 111 portmapper
0x31230000 1 tcp 45678 calc
Project 17: RPC with Authentication
- File: P17-rpc-auth.md
- Main Programming Language: C
- Coolness Level: Level 3 (Genuinely Clever)
- Business Potential: Level 3 (Service & Support)
- Difficulty: Level 3 (Advanced)
- Knowledge Area: Security, RPC
What youโll build: Extend the RPC calculator with AUTH_SYS (Unix) and AUTH_DES authentication.
Why it teaches IPC: Real RPC services need authentication. Youโll see how Sun RPCโs pluggable authentication works.
Project 18: Distributed Key-Value Store with RPC
- File: P18-rpc-kvstore.md
- Main Programming Language: C
- Coolness Level: Level 4 (Hardcore Tech Flex)
- Business Potential: Level 4 (Open Core)
- Difficulty: Level 4 (Expert)
- Knowledge Area: Distributed Systems, Storage
What youโll build: A multi-server key-value store using RPC for client-server and server-server communication.
Why it teaches IPC: This combines everything: RPC for remote calls, shared memory for local caching, semaphores for coordination.
Real World Outcome
# Start 3 server replicas
$ ./kv_server --id=1 --port=9001 --peers=localhost:9002,localhost:9003 &
$ ./kv_server --id=2 --port=9002 --peers=localhost:9001,localhost:9003 &
$ ./kv_server --id=3 --port=9003 --peers=localhost:9001,localhost:9002 &
# Client connects to any server
$ ./kv_client --server=localhost:9001 put mykey "Hello World"
OK (replicated to 3 nodes)
$ ./kv_client --server=localhost:9002 get mykey
Hello World
# Kill a server, data still available
$ kill %1
$ ./kv_client --server=localhost:9002 get mykey
Hello World
Project Comparison Table
| Project | Difficulty | Time | Depth of Understanding | Fun Factor |
|---|---|---|---|---|
| 1. Shell Pipeline | Level 2 | Weekend | High | โ โ โ โ โ |
| 2. FIFO Client-Server | Level 2 | Weekend | Medium | โ โ โ โโ |
| 3. Implement popen | Level 3 | 1 Week | High | โ โ โ โ โ |
| 4. POSIX MQ Dispatcher | Level 3 | 1 Week | High | โ โ โ โ โ |
| 5. System V MQ Server | Level 3 | 1 Week | High | โ โ โ โโ |
| 6. MQ Benchmark | Level 2 | Weekend | Medium | โ โ โ โโ |
| 7. Producer-Consumer | Level 3 | 1 Week | Very High | โ โ โ โ โ |
| 8. RWLock Implementation | Level 4 | 2 Weeks | Very High | โ โ โ โ โ |
| 9. Record Locking DB | Level 3 | 1 Week | High | โ โ โ โโ |
| 10. Semaphore Pool | Level 3 | 1 Week | High | โ โ โ โ โ |
| 11. System V Barrier | Level 3 | 1 Week | High | โ โ โ โโ |
| 12. SHM Ring Buffer | Level 4 | 2 Weeks | Very High | โ โ โ โ โ |
| 13. SHM Image Processor | Level 3 | 1 Week | Medium | โ โ โ โ โ |
| 14. mmap Database | Level 4 | 2 Weeks | Very High | โ โ โ โ โ |
| 15. Lock-Free Queue | Level 5 | 3 Weeks | Extreme | โ โ โ โ โ |
| 16. RPC Calculator | Level 3 | 1 Week | High | โ โ โ โโ |
| 17. RPC Authentication | Level 3 | 1 Week | Medium | โ โ โ โโ |
| 18. Distributed KV Store | Level 4 | 3 Weeks | Very High | โ โ โ โ โ |
Recommendation
If you are new to IPC: Start with Project 1 (Shell Pipeline). It uses only pipes and fork(), the most fundamental IPC primitives.
If you are a systems programmer: Start with Project 7 (Producer-Consumer). Mutexes and condition variables are the foundation of all concurrent code.
If you want high performance: Focus on Projects 12-15 (Shared Memory). This is where the real speed gains happen.
If you need distributed systems: Start with Projects 16-18 (RPC). Understanding Sun RPC helps you appreciate modern alternatives like gRPC.
Final Overall Project: Unix IPC Integration Test Suite
The Goal: Combine all your implementations into a comprehensive IPC benchmark and test harness.
- Create a unified library with all IPC mechanisms
- Build a benchmark CLI that compares them all
- Add stress tests that verify correctness under load
- Generate reports with performance comparisons
- Document the trade-offs you discovered
Success Criteria: You can run ./ipc_suite benchmark --all and get a complete comparison of every IPC mechanism, with correctness verification and performance metrics.
From Learning to Production: Whatโs Next?
After completing these projects, youโve built educational implementations. Hereโs how to transition to production-grade systems:
What You Built vs. What Production Needs
| Your Project | Production Equivalent | Gap to Fill |
|---|---|---|
| Shared Memory Ring Buffer | LMAX Disruptor, io_uring | Lock-free MPMC, batch operations |
| Message Queue Dispatcher | RabbitMQ, ZeroMQ | Persistence, clustering, acknowledgments |
| RPC Calculator | gRPC, Apache Thrift | HTTP/2, streaming, load balancing |
| mmap Database | LMDB, SQLite | B-trees, ACID transactions, crash recovery |
Skills You Now Have
You can confidently discuss:
- The trade-offs between POSIX and System V IPC
- When to use shared memory vs message passing
- How to avoid deadlocks and race conditions
- Memory ordering and lock-free programming basics
You can read source code of:
- PostgreSQL (shared buffers, semaphores)
- Redis (client-server, fork for persistence)
- Nginx (worker processes, shared memory)
- ZeroMQ (message queues, patterns)
You can architect:
- Multi-process servers with shared state
- High-performance data pipelines
- Distributed systems with RPC
Recommended Next Steps
1. Contribute to Open Source:
- libuv: Node.jsโs cross-platform IPC layer
- ZeroMQ: Modern message queue patterns
2. Build Production Systems:
- Replace Sun RPC with gRPC in Project 18
- Add persistence to your message queue
- Implement a proper B-tree for the mmap database
3. Get Certified:
- Linux Foundation Certified System Administrator (LFCS)
- Linux Foundation Certified Engineer (LFCE)
Career Paths Unlocked
With this knowledge, you can pursue:
- Systems Programming (databases, file systems)
- Infrastructure Engineering (containers, orchestration)
- Performance Engineering (low-latency trading, games)
- Distributed Systems (cloud platforms, data pipelines)
Summary
This learning path covers Unix IPC through 18 hands-on projects.
| # | Project Name | Main Language | Difficulty | Time Estimate |
|---|---|---|---|---|
| 1 | Shell Pipeline Executor | C | Level 2 | Weekend |
| 2 | FIFO Client-Server | C | Level 2 | Weekend |
| 3 | Implement popen | C | Level 3 | 1 Week |
| 4 | POSIX MQ Dispatcher | C | Level 3 | 1 Week |
| 5 | System V MQ Server | C | Level 3 | 1 Week |
| 6 | MQ Benchmark | C | Level 2 | Weekend |
| 7 | Producer-Consumer | C | Level 3 | 1 Week |
| 8 | RWLock Implementation | C | Level 4 | 2 Weeks |
| 9 | Record Locking DB | C | Level 3 | 1 Week |
| 10 | Semaphore Pool | C | Level 3 | 1 Week |
| 11 | System V Barrier | C | Level 3 | 1 Week |
| 12 | SHM Ring Buffer | C | Level 4 | 2 Weeks |
| 13 | SHM Image Processor | C | Level 3 | 1 Week |
| 14 | mmap Database | C | Level 4 | 2 Weeks |
| 15 | Lock-Free Queue | C | Level 5 | 3 Weeks |
| 16 | RPC Calculator | C | Level 3 | 1 Week |
| 17 | RPC Authentication | C | Level 3 | 1 Week |
| 18 | Distributed KV Store | C | Level 4 | 3 Weeks |
Expected Outcomes
After completing these projects, you will:
- Understand every IPC mechanism in Unix at the system call level
- Know exactly when to use pipes vs message queues vs shared memory
- Be able to debug race conditions and deadlocks systematically
- Have performance intuition for each IPC mechanism
- Be prepared for systems programming interviews at any company
Youโll have built a complete IPC toolkit from first principles, covering everything in Stevensโ Unix Network Programming Volume 2.
Additional Resources & References
Standards & Specifications
Online Guides
- Beejโs Guide to IPC - Excellent free online resource
- Opensource.com IPC Series
Books
IPC Specific:
- โUnix Network Programming Volume 2โ by W. Richard Stevens (Prentice Hall) - THE definitive reference
- โThe Linux Programming Interfaceโ by Michael Kerrisk (No Starch Press) - Modern Linux-specific coverage
Concurrency:
- โProgramming with POSIX Threadsโ by David Butenhof - Pthreads bible
- โRust Atomics and Locksโ by Mara Bos - Modern take on lock-free programming
Foundations (from your library):
- โAdvanced Programming in the UNIX Environmentโ by Stevens & Rago - System programming context
- โOperating Systems: Three Easy Piecesโ by Arpaci-Dusseau - Theoretical foundation
- โComputer Systems: A Programmerโs Perspectiveโ by Bryant & OโHallaron - Understanding the machine
This guide covers every IPC mechanism from W. Richard Stevensโ โUnix Network Programming Volume 2: Interprocess Communications.โ Work through all 18 projects to achieve complete mastery of Unix IPC.