Because processes have isolated address spaces (see Processes and Threads), they cannot share variables directly. Inter-process communication (IPC) is the set of OS mechanisms that let processes exchange data and coordinate.
Two Models
- Message passing: the OS copies data between processes. Clean isolation, but a copy per message.
- Shared memory: the OS maps the same physical frames into two address spaces. Fast (no per-message copy), but the processes must synchronize themselves.
message passing: P1 --copy--> [kernel buffer] --copy--> P2
shared memory: P1 -\ /- P2
> same physical frames (mapped) <Pipes
A pipe is a unidirectional in-kernel byte stream with a read end and a write end.
int fd[2];
pipe(fd); // fd[0] = read end, fd[1] = write end
if (fork() == 0) { // child writes
close(fd[0]);
write(fd[1], "hi", 2);
} else { // parent reads
close(fd[1]);
char buf[16];
read(fd[0], buf, sizeof buf);
}The shell builds ls | wc by connecting one process’s stdout to the next’s stdin through a pipe. Named pipes (FIFOs) have a filesystem path, so unrelated processes can rendezvous.
Pipe pitfalls
A pipe has a finite kernel buffer: writing to a full pipe blocks, reading an empty one blocks. Writing to a pipe whose read end is closed raises SIGPIPE (default: kill). Both ends must close unused descriptors, or a reader never sees end-of-file.
Message Queues
A message queue holds discrete, typed messages that persist in the kernel until read, decoupling sender and receiver in time.
msgsnd/msgrcv(System V) ormq_send/mq_receive(POSIX).- Receivers can select messages by type or priority.
- Send/receive can be blocking or non-blocking, and communication can be synchronous (rendezvous) or asynchronous (buffered).
Shared Memory
The fastest IPC: map a region into multiple address spaces and access it like ordinary memory.
int fd = shm_open("/region", O_CREAT | O_RDWR, 0600);
ftruncate(fd, 4096);
void *p = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
// both processes now read/write *p directlyShared memory needs synchronization
The kernel gives you the shared frames but no coordination. Concurrent access races exactly as threads do, so you must add a mutex, semaphore, or condition variable (see Synchronization), typically placed inside the shared region itself.
Sockets
Sockets are the general endpoint abstraction, working both within one host and across a network.
- Domains: UNIX domain sockets (same host, fast) and INET sockets (TCP/UDP over IP).
- TCP: connection-oriented, reliable, ordered byte stream (
socket,bind,listen,accept,connect,send,recv). - UDP: connectionless datagrams, no delivery guarantee, lower latency.
Sockets are the basis of the client-server model and, unlike pipes and shared memory, extend naturally beyond a single machine.
Signals
A signal is an asynchronous software interrupt delivered to a process, carrying only a small integer number (no payload).
| Signal | Meaning | Default action |
|---|---|---|
| SIGINT | interrupt (Ctrl-C) | terminate |
| SIGKILL | kill (cannot be caught) | terminate immediately |
| SIGSEGV | invalid memory access | terminate + core dump |
| SIGCHLD | a child changed state | ignored |
| SIGTERM | polite termination request | terminate |
void handler(int sig) { /* keep it tiny and async-signal-safe */ }
signal(SIGINT, handler); // or sigaction() for reliable semanticsSignal handlers are dangerous
A handler runs at an arbitrary point in the program, so it may interrupt a non-reentrant function mid-update. Only call async-signal-safe functions inside a handler (for example,
write, notprintformalloc). The common safe pattern is to set avolatile sig_atomic_tflag and do the real work back in the main loop.
Choosing a Mechanism
| Need | Use |
|---|---|
| Stream between related procs | pipe |
| Discrete, decoupled messages | message queue |
| Highest throughput, one host | shared memory + a lock |
| Across the network | sockets |
| Notify of an event/condition | signal |
All of these rest on system calls that trap into the kernel; the trap mechanism is detailed in OS Hardware.