Inter-Process Communication (IPC)

[!NOTE] This module explores the core principles of Inter-Process Communication (IPC), deriving solutions from first principles and hardware constraints to build world-class, production-ready expertise.

1. The Isolation Problem

Processes are isolated by design. Process A cannot access Process B’s memory. This prevents crashes and security breaches. But sometimes, they need to talk.

2. Mechanisms of IPC

1. Pipes (The Unix Way)

A unidirectional data channel.

  • Anonymous Pipe: ls | grep txt. Used between parent and child.
  • Named Pipe (FIFO): A file on disk that acts as a pipe. Any process can open it.
  • Pros: Simple, stream-oriented.
  • Cons: Unidirectional, byte-stream (no structure).

2. Sockets (The Network Way)

Bidirectional communication endpoints.

  • Unix Domain Sockets: Local file path (Fast).
  • TCP/UDP Sockets: Network IP:Port (Universal).
  • Pros: Standard for networked apps.
  • Cons: Overhead (Networking stack).

3. Shared Memory (The Fast Way)

The OS maps the same physical RAM pages into the virtual address space of two processes.

  • Pros: Zero-copy. Extremely fast.
  • Cons: Race Conditions. You need synchronization (Mutexes/Semaphores) or data corruption occurs.

4. Signals (The Minimal Way)

Tiny notifications sent by the OS to a process.

  • SIGINT (Ctrl+C): Interrupt.
  • SIGKILL (Kill -9): Die immediately.
  • SIGSEGV: Segmentation Fault (Invalid memory access).

3. Interactive: The Pipe Plumber

Visualize how data flows through a pipe buffer. Pipes have limited capacity!

Writer
Process A
Kernel Buffer (Limited Size)
Reader
Process B
Buffer Empty.

4. Code Example: Go Channels

Go improves IPC by making “Communicating Sequential Processes” (CSP) a first-class citizen. Channels are essentially typed, synchronized pipes.

```go package main import ( "fmt" "time" ) func main() { // Create a channel (like a pipe) // Buffered: Can hold 2 items before blocking writer messages := make(chan string, 2) // Producer (Goroutine) go func() { fmt.Println("Writer: Sending 'ping'...") messages <- "ping" fmt.Println("Writer: Sending 'pong'...") messages <- "pong" fmt.Println("Writer: Sending 'done' (Will Block if buffer full)...") messages <- "done" fmt.Println("Writer: Unblocked!") close(messages) }() // Consumer (Main Thread) time.Sleep(1 * time.Second) // Let buffer fill fmt.Println("Reader: Starting to read...") for msg := range messages { fmt.Println("Reader: Received", msg) time.Sleep(500 * time.Millisecond) } } ```