Inter-Process Communication

IPC Overview

Inter-process communication (IPC) refers to OS-supported mechanisms that allow multiple processes to synchronize, coordinate, and communicate.

Two broad categories:

  • Message-based IPC: sockets, pipes, message queues

  • Memory-based IPC: shared memory, memory-mapped files

Other higher-level IPC mechanisms include Remote Procedure Calls (RPC) and synchronization primitives (mutexes, semaphores).

Message-Based IPC

Processes create messages and send/receive them through OS-managed channels (buffers, FIFO queues).

  • OS kernel establishes the channel and performs every IPC operation

  • Both send and receive require a system call + data copy

  • A simple request-response = 4 user/kernel crossings + 4 data copies

  • Drawback: overhead of kernel crossings and data copies

  • Benefit: simplicity — kernel handles channel management and synchronization

Forms of Message Passing

Pipes:

  • Two endpoints only; no message structure — just a byte stream

  • Commonly connect stdout of one process to stdin of another

  • Part of POSIX standard

Message Queues:

  • Understand message structure; support priorities and scheduling

  • APIs: POSIX and System V

Sockets:

  • The port abstraction serves as the message-passing endpoint

  • send() / recv() move data in/out of kernel socket buffer

  • Socket creation associates kernel-level protocol processing (e.g., TCP/IP stack)

  • Work for both inter-machine and intra-machine communication

  • OS optimizes local communication by bypassing full protocol stack

Shared Memory IPC

Processes read/write a shared memory region mapped into both address spaces.

  • OS maps the same physical pages into virtual address spaces of both processes

  • Virtual addresses in each process may differ; physical memory need not be contiguous

  • After setup, OS is out of the way — system calls only during setup phase

  • Data copies potentially reduced (not always eliminated — data must be explicitly allocated from the shared region)

Drawbacks:

  • Processes must explicitly synchronize access (like threads sharing an address space)

  • Developer responsible for communication protocol: message format, delimiters, headers, buffer allocation

APIs:

  • System V shared memory API

  • POSIX shared memory API (uses file-based interface via tmpfs)

  • Android Ashmem

Performance Comparison

  • Message passing: multiple data copies between processes and kernel → overhead

  • Shared memory: costly kernel mapping setup, but amortized over many uses

  • For large data transfers, memory mapping can outperform copying

  • Windows Local Procedure Calls (LPC): uses copy for small data (below threshold), memory mapping for large data

System V Shared Memory API

  • OS manages segments of shared memory (non-contiguous physical pages)

  • System-wide limits (Linux currently: ~4000 segments)

  • Segments are persistent until explicitly destroyed or system reboot

Key operations:

  • shmget(key, size, flags) — create/open a segment

  • ftok(pathname, id) — generate a unique key (hash-like)

  • shmat(shmid, addr, flags) — attach segment to process address space (returns virtual address; pass NULL for OS-chosen address)

  • shmdt(addr) — detach segment (invalidates page table entries)

  • shmctl(shmid, IPC_RMID, ...) — destroy segment

POSIX Shared Memory API

  • Uses files instead of segments (in tmpfs — volatile, memory-backed)

  • No awkward key generation; segments referenced by file descriptor

  • shm_open() / shm_close() — open/close shared memory

  • mmap() / munmap() — attach/detach

  • shm_unlink() — destroy the shared memory segment

  • Supported since Linux kernel 2.4

Synchronization with Shared Memory

Shared memory requires explicit synchronization (like multi-threaded programs).

PThreads Synchronization for IPC

  • Use PTHREAD_PROCESS_SHARED attribute on mutexes/condition variables

  • Critical: synchronization variables must be allocated within the shared memory region

// Shared memory data structure
typedef struct {
    pthread_mutex_t mutex;
    char data[BUF_SIZE];
} shm_data_t;

// Setup
seg_id = shmget(ftok(argv[0], 120), 1024, IPC_CREAT | perms);
shm_addr = (shm_data_t *) shmat(seg_id, NULL, 0);

// Initialize process-shared mutex
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED);
pthread_mutex_init(&shm_addr->mutex, &attr);

Other Synchronization for IPC

  • Message queues: implement mutual exclusion via send/receive protocol (P1 writes, sends “ready”; P2 receives, reads, sends “OK”)

  • Semaphores: binary semaphore (0/1) behaves like a mutex; value 0 = blocked, value 1 = decrement and proceed

IPC Command Line Tools

  • ipcs — list all IPC facilities (-m for shared memory only)

  • ipcrm -m <shmid> — remove a shared memory segment

Shared Memory Design Considerations

Number of segments:

  • One large segment → need internal memory management (allocator)

  • Multiple small segments (one per pair-wise communication) → preallocate a pool; use a queue of segment IDs for assignment

Segment size:

  • Fixed size works if data size is static and known

  • For arbitrary/large messages: transfer in rounds with a protocol tracking progress

  • Shared memory structure typically includes: data buffer + synchronization construct + progress flags