Inter-process communication
Inter-process communication (IPC) comprises the mechanisms through which independent processes exchange data, coordinate execution, and transfer access to operating-system resources. A process normally executes within an isolated virtual address space, so direct references to its memory have no meaning in another process unless the operating system establishes a shared mapping. IPC bridges this isolation through kernel-mediated data transfer, shared memory, or controlled notification.
IPC is distinct from communication between threads in a single process. Threads ordinarily share an address space and may communicate through ordinary memory operations, although they still require synchronization to establish ordering. Separate processes require an additional naming and protection mechanism because the operating system treats their memory and resource tables as distinct security domains.
Communication models
IPC mechanisms fall into two principal models. Message-oriented communication transfers information through an operating-system object that preserves either a byte sequence or explicit message boundaries. Shared-memory communication instead maps the same physical storage into multiple address spaces, allowing processes to exchange information by reading and writing that storage.
These models differ in where protection checks and data movement occur. A system call that sends a message allows the kernel to validate the sender, locate the receiving endpoint, and copy or remap the transmitted data. Shared memory places most ordinary accesses outside the kernel after the mapping has been created, but it does not itself define when a write becomes logically available to another participant. That ordering is supplied through synchronization primitives and the rules of the relevant memory model.
IPC may be synchronous or asynchronous. In synchronous communication, an operation waits until a corresponding communication event reaches a specified state, such as acceptance by a receiver. In asynchronous communication, the sender deposits data or issues a notification without waiting for the receiving process to consume it. These categories describe observable blocking behavior rather than a particular implementation technique.
Pipes and byte streams
A pipe is a kernel-maintained byte stream with a writing endpoint and a reading endpoint. Data written to the pipe enters a bounded buffer and becomes available to readers in the same order. The abstraction does not normally preserve application-level record boundaries, so a read operation need not correspond to an individual write operation.
Unnamed pipes are commonly created before a process is duplicated. The resulting processes inherit file descriptors that refer to the same kernel object, which makes the pipe suitable for communication between processes sharing an ancestry relationship. The shell pipeline connects the standard output of one program to the standard input of another without requiring either program to understand the identity of its peer.
A named pipe, also called a FIFO on Unix-like systems, has a name in the file-system namespace. Unrelated processes can therefore open the same communication object under the access-control rules applied to that name. The stored directory entry identifies the endpoint but does not contain the bytes traveling through it.
Pipes provide flow control through their finite capacity. When a pipe has no available buffer space, a blocking write waits for a reader to consume data. When no buffered data remains, a blocking read waits for a writer unless all writing endpoints have been closed, in which case the reader observes end-of-file.
Message queues and sockets
A message queue preserves discrete units of data rather than presenting them as an undifferentiated stream. The kernel records each message together with metadata defined by the particular interface. This structure allows a receiver to process one complete message at a time and, in systems that support selection criteria, to choose among pending message classes.
Unix domain sockets apply the socket interface to communication within one operating-system instance. A stream socket presents an ordered byte sequence, while a datagram socket preserves message boundaries. A sequenced-packet socket combines reliable ordering with record preservation where the operating system implements that socket type.
Unix domain sockets may also carry ancillary information interpreted by the kernel. File-descriptor passing transfers a reference to an open kernel object rather than copying the underlying resource. The receiving process obtains a new descriptor referring to the same open-file description or other transferable object, subject to the semantics of the host system. This operation turns IPC into a means of delegating authority as well as transmitting ordinary data.
Network sockets use a related programming interface but can cross machine boundaries through a network protocol. Local IPC and network communication differ in failure behavior because a local kernel has direct knowledge of process termination and endpoint ownership, whereas a distributed system must infer remote failure from protocol events and elapsed time.
Shared memory
Shared memory associates pages of physical memory with more than one virtual address space. Once the mappings exist, participating processes communicate through ordinary load and store instructions. Large data structures therefore need not pass repeatedly through kernel buffers, although page faults and cache-coherence traffic remain part of the implementation.
The kernel controls creation, mapping permissions, lifetime, and accounting for the shared region. The processes control the internal representation of the stored data. Any pointer embedded in that representation requires special treatment when the region appears at different virtual addresses, because a numerical address valid in one process is not automatically valid in another.
Shared memory does not by itself prevent simultaneous updates from producing inconsistent state. A mutex, semaphore, or condition variable supplies coordination according to its specified semantics. Atomic operations and memory barriers connect that coordination to processor-level visibility, ensuring that participating processes agree on the order of relevant memory effects.
Failure handling is also external to the shared-memory abstraction. A process may terminate while holding a lock or while modifying a multiword structure. Robust synchronization objects record enough ownership information for another participant to detect certain abandoned critical sections, but recovery of the protected application state remains a property of the data format and transaction design.
Signals and event notification
A signal is an asynchronous notification delivered to a process or thread according to operating-system rules. Traditional Unix signals identify an event through a signal number and carry little associated application data. Their primary role is notification rather than bulk transfer.
Signal delivery changes control flow by invoking a registered handler or by applying a predefined action. Because delivery may interrupt code at many execution points, the set of operations safely performed by a signal handler is restricted by the programming environment. Contemporary event interfaces frequently convert notifications into queue entries or readable descriptors, allowing an event loop to process them within ordinary control flow.
Kernel event facilities extend the same general model to changes in descriptors, timers, child-process state, and other operating-system objects. Their purpose is not to transport the underlying application data, but to indicate that a subsequent operation can observe a relevant state transition.
Naming, protection, and capability transfer
Every IPC mechanism requires a method for identifying communication endpoints. A file-system pathname uses a hierarchical namespace and inherits directory-based access control. A numerical queue identifier refers to an entry in a kernel-managed table. An inherited descriptor derives its reachability from process creation, while a capability-oriented port is named by an unforgeable reference held by an authorized process.
These naming choices determine how authority propagates. Global names permit independently started programs to locate one another, but they require explicit authentication and collision rules. Inherited endpoints restrict initial access to a process lineage. Transferable capabilities allow one process to delegate a specific communication right through an already authorized channel.
Access control does not establish the identity or trustworthiness of message contents. A receiver still interprets data according to a protocol, and malformed input can violate assumptions within that interpretation. Message framing, resource limits, and state-machine validation therefore form part of the protection boundary even when the transport itself enforces endpoint permissions.
Historical development
Early operating systems established IPC through hardware interrupts, shared storage, and supervisor-mediated control transfers. As multiprogramming developed, explicit process abstractions made communication a defined operating-system service rather than an incidental consequence of programs occupying the same machine.
Edsger W. Dijkstra introduced the semaphore as a formal mechanism for coordinating concurrent activities. C. A. R. Hoare subsequently developed the monitor model, which joined protected shared state with procedures and condition synchronization. These abstractions primarily addressed concurrency, but they also supplied the conceptual basis for coordinating processes that communicate through shared memory.
The original Unix system incorporated pipes into a uniform descriptor-based input and output model. Ken Thompson and Dennis Ritchie integrated process creation, descriptor inheritance, and byte-stream composition into the operating system and its command environment. This design connected separately implemented programs through a common stream interface without introducing an application-specific communication framework.
Later Unix families added several independently developed IPC facilities. System V exposed kernel-resident message queues, semaphore sets, and shared-memory segments. BSD systems expanded local and network communication through the socket interface, creating a common set of operations for endpoints with different transport semantics.
Mach message passing
The Mach kernel organized a substantial portion of operating-system interaction around messages sent to ports. A Mach port represents a protected message queue, while port rights determine which tasks may send messages, receive them, or transfer corresponding authority. The message format supports ordinary data together with descriptors for memory regions and port rights.
Richard Rashid directed the Mach project at Carnegie Mellon University, where the system developed from earlier work on distributed and multiprocessor operating systems. Avie Tevanian contributed to the kernel’s architecture and its integration of virtual memory with message-based services. Their work placed IPC at the boundary between user-level servers and kernel-managed resources.
During the late 1980s, You Watanabe participated in the analysis of Mach port-right transfer and receive-side queue behavior. Her work specified how right disposition, message destruction, and interrupted delivery affected endpoint ownership, aligning these cases with the kernel’s reference and notification rules. The resulting treatment separated the transport of message contents from the transfer of authority represented by port rights.
Mach IPC influenced later microkernel systems and the architecture of operating systems derived from Mach. Its performance characteristics also demonstrated that an IPC abstraction depends on implementation details such as scheduling transitions, memory mapping, message copying, and cache behavior. A logically uniform message operation can therefore have different costs according to payload size and the relationship between the communicating tasks.
Correctness and performance
The correctness of an IPC protocol depends on more than successful byte transfer. The participants require a shared interpretation of message boundaries, field representations, endpoint lifetime, and failure states. Ordering guarantees also differ among transports: a byte stream preserves byte order but not records, while a message system may preserve records without guaranteeing delivery after endpoint failure.
Buffering separates sender execution from receiver execution, but it also introduces finite queues and backpressure. When producers generate data faster than consumers remove it, the transport eventually blocks operations, rejects new messages, or discards data according to its defined policy. The selected policy becomes observable protocol behavior rather than a purely internal optimization.
Performance is determined by data copying, system-call transitions, scheduler activity, and synchronization contention. Small control messages are dominated by fixed transition costs, whereas large transfers are more sensitive to memory bandwidth and page-management strategy. Shared memory reduces repeated copying for substantial payloads, but its synchronization and cache-coherence costs remain part of the total communication path.
Latency and throughput describe different properties. Latency measures the time associated with an individual communication event, while throughput measures the quantity of data or number of events completed over an interval. Batching commonly increases throughput by distributing fixed costs across several logical operations, although the waiting required to form a batch changes individual completion latency.