Named pipe
A named pipe is a mechanism for inter-process communication (IPC) in operating systems, extending the traditional pipe concept by assigning a persistent name to the communication channel, which enables data exchange between unrelated processes rather than just parent-child relationships.[1][2] In Unix-like systems, named pipes—also called FIFOs (first-in, first-out)—are special files created in the filesystem using commands like mkfifo, providing a unidirectional stream where data flows sequentially from writers to readers without buffering beyond a system-defined limit.[1] On Microsoft Windows, named pipes operate in a client-server model, supporting one-way or duplex communication across local or network boundaries, with the pipe server creating instances identifiable by unique names in the system's object namespace.[2][3] This naming allows processes to rendezvous and connect dynamically, making named pipes suitable for scenarios requiring structured IPC, such as in distributed applications or device drivers, while inheriting pipe semantics like blocking reads until data is available.[4][5] Unlike anonymous pipes, which are temporary and process-specific, named pipes persist until explicitly removed, facilitating broader usability but also introducing security considerations like access control via file permissions in Unix or security descriptors in Windows.[1][6]
Introduction
Definition
A named pipe, also known as a FIFO (first-in, first-out) special file in Unix-like systems, is an inter-process communication (IPC) mechanism that enables unrelated processes to exchange data via a persistent, named entry in the filesystem, presenting a file-like interface for standard I/O operations.[7][2] It functions as a one-way or bidirectional channel, depending on the system, where data flows sequentially from writer to reader without intermediate storage on disk.[7][2]
Named pipes manifest as special files within the filesystem, identifiable by their type and accessible to any process with sufficient permissions using conventional file descriptors obtained through functions like open().[8][9] This allows multiple processes to connect to the same pipe endpoint, facilitating data streaming in a FIFO manner where the order of writes is preserved in reads.[7][9]
Central properties include inherent blocking semantics: an attempt to open the pipe for reading blocks until at least one writer connects, and vice versa for writing, ensuring synchronization between endpoints unless non-blocking mode is specified.[8][9] They support continuous streaming of data between connected processes and remain persistent in the filesystem until explicitly unlinked or removed, outlasting the processes that created or used them.[9]
In contrast to anonymous pipes, which lack filesystem visibility and are limited to related processes within a single session, named pipes provide a discoverable name that permits independent processes to locate and utilize them for communication.[7][2]
Purpose and Use Cases
Named pipes serve as a mechanism for interprocess communication (IPC) between unrelated processes that do not share a common ancestor, enabling data exchange in scenarios where processes operate independently without relying on shared memory.[10] They support a server-client model, where one process functions as a writer or server, creating the pipe and sending data, while one or more reader or client processes connect to receive it, often using a blocking connection until both ends are ready.[2] This approach decouples the communicating processes, allowing them to run asynchronously and connect dynamically via a filesystem-visible name rather than requiring predefined process identifiers.[4]
In practice, named pipes are commonly employed in shell scripting to establish persistent pipelines between commands or scripts, facilitating modular data processing workflows beyond the limitations of anonymous pipes.[11] Daemon processes frequently utilize them for logging, where the daemon writes output to a named pipe and a separate analyzer or viewer process reads from it in real time, centralizing logs from multiple sources without intermediate files.[12] Client-server applications, such as database connectors, leverage named pipes for efficient local IPC; for instance, Microsoft SQL Server uses them to allow client applications to connect to the database instance via a named pipe endpoint.[13]
Additional scenarios include debugging tools that pipe diagnostic output to named pipes for consumption by monitoring processes, and system event handling where a tool reads events streamed from a named pipe by a kernel or user-space generator.[14] These use cases highlight the modularity of named pipes, as the named interface permits flexible, on-demand connections among processes in diverse environments like Unix-like systems and Windows.[15]
Technical Implementation
In Unix-like Systems
In Unix-like systems, named pipes are implemented as FIFO (first-in, first-out) special files, which provide a mechanism for interprocess communication through the filesystem. These special files are created using the mkfifo() system call or the corresponding mkfifo utility, which establishes a new FIFO at a specified pathname with permissions derived from the provided mode argument, adjusted by the process's umask. The file's user ID is set to the effective user ID of the creating process, while the group ID follows the parent directory's group or the process's effective group ID, depending on implementation details. Unlike regular files, FIFOs do not store data on disk; instead, they act as conduits where data written by one process is read by another in the order it was written.[16]
File permissions and ownership for these FIFO special files are managed like those of regular files, inheriting the creator's permissions and allowing access based on the filesystem path. Processes with appropriate read or write permissions can open the FIFO via its path using standard file descriptors, enabling unrelated processes to communicate without shared ancestry, as long as they can resolve the pathname. Ownership can be modified post-creation using functions like chown(), ensuring secure access control in multi-user environments. This integration with the filesystem namespace distinguishes Unix-like named pipes from anonymous pipes, making them discoverable and manageable like other files.[16][17]
By default, FIFOs operate in a one-way manner, with data flowing from writers to readers. The open() system call exhibits blocking behavior: an attempt to open for reading (O_RDONLY) blocks until at least one process opens the FIFO for writing, and vice versa for writing (O_WRONLY). This ensures synchronization between communicating parties. If the O_NONBLOCK flag is set, the open for reading succeeds immediately even without a writer, while an open for writing fails with ENXIO if no reader is present. Once opened, data is transferred using the read() and write() system calls, which behave atomically for writes up to PIPE_BUF bytes (4096 bytes in Linux; 512 bytes in FreeBSD and other BSD systems; at least 512 bytes as per POSIX, implementation-defined). Reading from an empty FIFO blocks until data arrives or all writers close, unless O_NONBLOCK is set, in which case it returns EAGAIN. Similarly, writing blocks if the FIFO is full or lacks readers. POSIX specifies that opening a FIFO for both reading and writing (O_RDWR) is undefined, though some implementations permit it. In modern implementations, the buffer capacity for FIFOs can be adjusted using the fcntl(2) system call with the F_SETPIPE_SZ command, allowing sizes up to implementation-defined limits (e.g., 1 MB in Linux).[8][18][19]
POSIX compliance for creating FIFOs also allows the use of mknod() with the S_IFIFO mode flag and a device number of zero, though this is the only portable application of mknod() for FIFOs; other uses are unspecified. Reading and writing proceed via the standard read() and write() interfaces, treating the FIFO as a stream-oriented device. To remove a FIFO, unlink() is used, which deletes the directory entry; if processes still hold the file open, the underlying object persists until all references are closed, at which point resources are freed. No special handling is required beyond standard file unlinking.[20][21]
Variants exist across Unix-like systems, with Linux and BSD implementations closely adhering to POSIX but featuring subtle differences. In Linux, FIFOs can be created anywhere in the filesystem, including under /dev/ if permissions allow, though no dedicated /dev/fifo device exists; the kernel treats them uniformly as special files with detailed semantics in the fifo(7) manual, including support for opening in read-write mode (O_RDWR) without failure, which enables self-communication but risks deadlocks—a behavior left undefined by POSIX. BSD systems, such as FreeBSD, implement FIFOs similarly through mkfifo and mknod, with equivalent blocking opens and I/O semantics, but emphasize stricter adherence to POSIX without the Linux-specific RDWR allowance, relying on the same core utilities and system calls for creation, access, and removal. These differences primarily affect edge cases like bidirectional opens, but core functionality remains consistent across distributions.[9][22]
In Microsoft Windows
In Microsoft Windows, named pipes are implemented as kernel objects within the NT kernel, providing a mechanism for interprocess communication through the Named Pipe File System (NPFS).[23] The server creates an instance of a named pipe using the CreateNamedPipe API function, which returns a HANDLE to the server end of the pipe for subsequent operations such as reading, writing, or connecting clients.[24] This API supports bidirectional communication when the dwOpenMode parameter is set to PIPE_ACCESS_DUPLEX, allowing both the server and client to perform read and write operations on the same pipe instance.[24]
The naming convention for named pipes follows the format \\.\pipe\pipename for local pipes, where pipename is a custom string up to 256 characters long, case-insensitive, and without backslashes; for remote pipes, it uses \\ServerName\pipe\pipename.[6] These names are not visible or accessible through the standard filesystem, as named pipes exist as system objects rather than files.[6] A single pipe name can support multiple instances, enabling the server to create several concurrent connections from different clients, each with its own buffer and handle.[2]
Security for named pipes integrates with the Windows access control model, where the lpSecurityAttributes parameter in CreateNamedPipe points to a SECURITY_ATTRIBUTES structure specifying a security descriptor, including discretionary access control lists (ACLs) to control access rights such as FILE_CREATE_PIPE_INSTANCE.[24][25] The default security descriptor grants full control to the LocalSystem account, administrators, and the pipe creator, while providing read access to Everyone and anonymous users.[25] Additionally, the server can impersonate the connected client's security context using the ImpersonateNamedPipeClient function, which assumes the client's access token to perform operations on the client's behalf, such as accessing resources with the client's privileges; this requires the impersonation level to be at least SecurityImpersonation.[26][27]
By default, named pipe operations are blocking, as specified by the PIPE_WAIT mode in the dwPipeMode parameter of CreateNamedPipe, causing functions like ReadFile, WriteFile, and ConnectNamedPipe to wait indefinitely until the operation completes or a client connects.[28] The read mode can be configured as message-mode (PIPE_READMODE_MESSAGE), where data is read in discrete message units and a read operation fails with ERROR_MORE_DATA if the buffer is too small to hold an entire message, or byte-mode (default for byte pipes), which treats data as a continuous stream.[28] For non-blocking behavior, overlapped I/O can be used with the OVERLAPPED structure in read/write operations, allowing asynchronous handling without the PIPE_NOWAIT mode, which is not recommended for overlapped scenarios.[28]
Cleanup of named pipe instances occurs through the CloseHandle function, which the server calls to release its handle after disconnecting a client via DisconnectNamedPipe; clients similarly close their handles.[5] When the last handle to all instances of a named pipe is closed, the pipe is automatically deleted by the system, with no explicit deletion API required, and any unread data in the buffers is discarded.[5]
Creating and Using Named Pipes
Creation Methods
Named pipes are created through a series of steps that involve specifying a unique name for the pipe, defining access permissions or modes, instantiating the pipe object within the operating system, and managing potential errors during the process. The name serves as an identifier in the filesystem or namespace, permissions control read/write access, and creation typically blocks until a counterpart process connects unless configured otherwise. If the specified name already exists, creation fails with an error like EEXIST on Unix-like systems.
In Unix-like systems, named pipes, also known as FIFOs, can be created using the mkfifo command or the corresponding C library function. The command-line approach is mkfifo /tmp/mypipe, which creates a FIFO at the specified path with default permissions. For programmatic creation in C, the mkfifo function is used as follows:
c
#include <sys/types.h>
#include <sys/stat.h>
if (mkfifo("/tmp/mypipe", 0666) == -1) {
// Handle [error](/page/Error)
}
#include <sys/types.h>
#include <sys/stat.h>
if (mkfifo("/tmp/mypipe", 0666) == -1) {
// Handle [error](/page/Error)
}
This sets the permissions to read/write for owner, group, and others (0666 in octal), and returns 0 on success or -1 on failure. Note that mkfifo creates the FIFO but does not open it; a separate open() call is needed to obtain a file descriptor.[29]
On Microsoft Windows, named pipes are created using the CreateNamedPipe API function in C/C++, which establishes a server instance of the pipe. A basic example for an inbound byte-stream pipe is:
c
#include <windows.h>
HANDLE hPipe = CreateNamedPipe(
TEXT("\\\\.\\pipe\\mypipe"), // Pipe name
PIPE_ACCESS_INBOUND, // Read-only access
PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, // Byte stream mode
1, // Max instances
1024, // Output buffer size
1024, // Input buffer size
0, // Default timeout
NULL // Default security attributes
);
if (hPipe == INVALID_HANDLE_VALUE) {
// Handle error
}
#include <windows.h>
HANDLE hPipe = CreateNamedPipe(
TEXT("\\\\.\\pipe\\mypipe"), // Pipe name
PIPE_ACCESS_INBOUND, // Read-only access
PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, // Byte stream mode
1, // Max instances
1024, // Output buffer size
1024, // Input buffer size
0, // Default timeout
NULL // Default security attributes
);
if (hPipe == INVALID_HANDLE_VALUE) {
// Handle error
}
This creates a pipe named \\.\pipe\mypipe with specified buffer sizes and returns a handle on success.
For cross-platform development, higher-level libraries abstract these OS-specific calls. In Python, the os.mkfifo function creates a named pipe on Unix-like systems with os.mkfifo("/tmp/mypipe", 0o666), returning None on success, though it raises OSError on failure and is not directly supported on Windows. On Windows and cross-platform, .NET's NamedPipeServerStream class is used as var pipe = new NamedPipeServerStream("mypipe", PipeDirection.In, 1);, which handles creation and returns a stream object.
Error handling during creation is crucial and varies by platform, including checks for permission denied (EACCES on Unix, ERROR_ACCESS_DENIED on Windows) if the process lacks rights to the directory or name, or invalid name formats (e.g., exceeding path length limits or using reserved characters). On Unix, additional errors like ENAMETOOLONG arise for overly long names, while Windows may return ERROR_PIPE_BUSY if the name is in use. Developers must verify the return value and use system-specific functions like perror or GetLastError to diagnose issues.
Communication Process
In Unix-like systems, the communication process for named pipes, also known as FIFOs, begins with the server process opening the pipe for writing using the open() system call with O_WRONLY or O_RDWR flags, which blocks until at least one client opens it for reading.[1] The client then opens the same FIFO path for reading with O_RDONLY, unblocking the server's open operation and establishing the connection.[30] Once connected, data flows unidirectionally as a byte stream: the server performs sequential write() operations to send data, which the kernel buffers (up to 65,536 bytes since Linux 2.6.11), while the client uses read() to retrieve it, potentially handling partial reads if the buffer is not fully populated or using non-blocking mode via O_NONBLOCK for incomplete transfers.[1] An end-of-file (EOF) condition signals the client when all write ends are closed, causing read() to return zero bytes.
For bidirectional communication in Unix-like systems, two separate FIFOs are typically used—one for each direction—since named pipes are inherently unidirectional.[9] There are no distinct message modes; all data is treated as an unstructured byte stream without boundaries.[1] Termination occurs when processes close their file descriptors using close(), after which the kernel discards any remaining buffered data; the FIFO file persists in the filesystem until explicitly removed with unlink(). Error scenarios include the generation of a SIGPIPE signal (or EPIPE error if signals are ignored) when writing to a pipe with no open readers, and timeouts or EAGAIN errors in non-blocking mode if the buffer is full or empty.[1]
In Microsoft Windows, the server initiates the communication process by calling CreateNamedPipe() to instantiate the pipe and then ConnectNamedPipe() or WaitNamedPipe() to wait for a client connection, which can support multiple instances for concurrent clients. The client connects by invoking CreateFile() with the pipe's name, opening a handle if an instance is available or blocking until one is. Data exchange proceeds through sequential WriteFile() calls from the writer and ReadFile() from the reader, with the operating system handling buffering and potential partial transfers, similar to file I/O; an EOF is indicated when the writer closes its handle, prompting ReadFile() to return zero bytes.
Windows named pipes support two primary modes specified at creation: byte stream mode for unstructured, continuous data flow, and message mode (via PIPE_TYPE_MESSAGE) for discrete messages where ReadFile() and WriteFile() respect message boundaries, preventing partial reads or writes of messages. Pipes can be duplex, allowing bidirectional communication over a single instance.[2] Termination involves closing handles with CloseHandle(), which disconnects the client and decrements the instance count; when the count reaches zero and no outstanding instances remain, the pipe is automatically removed from the system. Common errors include access denied due to security descriptors, timeouts from WaitNamedPipe() if no server responds, or broken pipe errors (ERROR_BROKEN_PIPE) upon client disconnection during active I/O.[31]
Advantages and Disadvantages
Advantages
Named pipes offer simplicity in implementation due to their use of familiar file input/output (I/O) application programming interfaces (APIs), which lowers the learning curve for developers accustomed to standard file operations. In Unix-like systems, named pipes, or FIFOs, are created using the mkfifo utility or function, allowing processes to read and write as if interacting with regular files, without requiring specialized pipe-handling code.[32] Similarly, in Microsoft Windows, the CreateNamedPipe function enables server-side creation, while clients connect via CreateFile, leveraging the same I/O primitives for seamless integration into existing applications. This approach contrasts with more complex interprocess communication (IPC) mechanisms that demand unique APIs, making named pipes accessible for both related and unrelated processes.[2]
Security is a key strength of named pipes, as access control is enforced through established filesystem mechanisms, effectively preventing unauthorized reads or writes. On Unix-like systems adhering to POSIX standards, named pipes appear as special files in the filesystem, inheriting permissions managed by tools like chmod, which allow fine-grained control over read, write, and execute rights for users, groups, and others.[32] In Windows, named pipes support security descriptors and access control lists (ACLs) specified during creation, enabling administrators to restrict access to specific users or deny network connections for local-only communication, such as excluding the NT AUTHORITY\NETWORK account.[2] These features ensure that only authorized processes can connect, providing robust protection comparable to file-based security without additional configuration.
Named pipes provide efficiency for local IPC through kernel-mediated operations, incurring low overhead compared to network-oriented methods like sockets while supporting the transfer of large data streams. As filesystem objects, they avoid the protocol stack and buffering complexities of TCP/IP, resulting in faster performance for intra-machine communication; for instance, benchmarks show named pipes achieving approximately 30% higher throughput than Unix domain sockets for small block sizes in local scenarios.[33] In Windows, multiple pipe instances can handle concurrent clients with independent buffers, facilitating scalable, low-latency data exchange without user-mode copying.[2] This kernel-level handling minimizes context switches and memory allocations, making named pipes suitable for high-volume, unidirectional or bidirectional streams between processes on the same host.
The named nature of these pipes enhances discoverability, enabling dynamic process identification without reliance on external registries, ports, or predefined configurations. In Unix-like systems, the pipe's pathname in the filesystem serves as a persistent identifier, allowing any process to locate and connect to it via standard path resolution, similar to opening a file.[32] Windows named pipes use a similar naming convention in the namespace (e.g., \\.\pipe\pipename), where all instances share the name, permitting clients to easily find and connect to available servers without prior knowledge of process IDs.[2] This visibility supports ad-hoc communication in distributed applications, such as client-server models, where processes can discover endpoints at runtime.
Portability across major operating systems is facilitated by the widespread adoption of named pipes, bolstered by standards like POSIX that promote consistent behavior in Unix-like environments. POSIX.1 specifies the mkfifo interface for creating FIFOs with uniform semantics, ensuring compatibility across compliant systems such as Linux, macOS, and BSD variants.[32] In Windows, while the API differs, the conceptual model aligns closely, allowing developers to abstract pipe operations in cross-platform code using conditional compilation or libraries, thus enabling reusable IPC logic without major rewrites.[2] This standardization reduces vendor lock-in and supports deployment in heterogeneous environments.
Disadvantages
Named pipes exhibit platform dependencies that can complicate cross-system development and usage. In Unix-like systems, basic FIFOs are unidirectional, supporting only one-way communication unless additional mechanisms like paired FIFOs are employed, whereas Microsoft Windows named pipes natively support bidirectional communication.[9][2] Furthermore, behaviors such as opening a FIFO for both reading and writing are Linux-specific and may lead to undefined results under POSIX standards, increasing the risk of portability issues.[9]
The default synchronous operation of named pipes introduces blocking issues that can result in deadlocks. In blocking mode, operations such as connecting or reading/writing wait until completion, potentially stalling processes if both ends do not coordinate properly; non-blocking modes mitigate this but require explicit handling to avoid incomplete data transfers or timeouts.[34][9] This synchronous nature is particularly problematic in multi-threaded or multi-process scenarios without careful synchronization, as a writer may block indefinitely waiting for a reader that is itself blocked.[5]
Named pipes are inherently limited to local inter-process communication on the same machine, making them unsuitable for network-distributed systems. While Windows named pipes can be accessed remotely over SMB if the server service is enabled, this requires additional configuration and introduces security risks; in Unix-like systems, FIFOs are strictly filesystem-bound and cannot span machines without higher-level abstractions like sockets.[2][9]
Cleanup of named pipes poses challenges, as orphaned instances may persist and consume system resources if processes terminate abnormally. In Unix-like systems, named pipes appear as special files in the filesystem and must be manually removed using commands like rm even after all handles are closed, or they remain until explicitly deleted.[35] In Windows, named pipes are kernel objects that are automatically reclaimed when the last handle closes, but crashes or improper shutdowns can leave lingering instances detectable via tools like net file, necessitating manual intervention.[36]
Scalability of named pipes is limited for high-concurrency scenarios, as they typically support one-to-one or one-to-many communication patterns without built-in broadcast capabilities. In Unix, multiple readers on a single FIFO can lead to data interleaving or blocking contention on the shared buffer, while Windows allows multiple pipe instances for concurrent clients but incurs overhead from per-instance buffers and handle management, making it inefficient for large-scale, many-to-many interactions.[37][2]
Comparisons with Other IPC Methods
Versus Anonymous Pipes
Anonymous pipes, also known as unnamed pipes, are created without a persistent name and are primarily intended for communication between related processes, such as a parent and its child, typically following a fork operation in Unix-like systems or using CreatePipe in Windows.[38] In contrast, named pipes are assigned a specific name—stored in the filesystem in Unix-like systems via mkfifo or mknod, or in the system's object namespace under the Named Pipe File System (NPFS) at \.\pipe\ in Windows using CreateNamedPipe—allowing them to be accessed by unrelated processes without requiring a direct lineage.[39]
The fundamental difference lies in discoverability and persistence: anonymous pipes are ephemeral, existing only in memory as file descriptors passed between processes, and cannot be accessed by processes outside the immediate parent-child relationship, whereas named pipes function as named entities in the system namespace, enabling any process with appropriate permissions to open and connect to them independently.[38][40] This naming mechanism decouples communicating parties, as named pipes do not rely on inheritance through fork/exec sequences, making them suitable for scenarios involving multiple, independently launched processes.[41]
Anonymous pipes are commonly used for simple, linear data flows in shell pipelines, such as chaining commands like ls | grep pattern, where the output of one process directly feeds into the input of another related process.[38] Named pipes, however, support more complex, persistent inter-process communication, such as a server process waiting for connections from multiple client processes in a client-server architecture, even across different sessions or machines in Windows environments.[41]
In terms of trade-offs, anonymous pipes offer simplicity and lower overhead for short-lived, related-process interactions, as they avoid the need for filesystem or registry entries and can be faster to establish due to their in-memory nature.[41] Named pipes provide greater flexibility for decoupled and multi-process scenarios but incur additional overhead from name management and potential blocking on opens until both ends are connected, particularly in Unix-like FIFOs where a write-only open blocks until a reader is present.[39][38]
Versus Sockets
Named pipes and sockets represent two prominent mechanisms for interprocess communication (IPC), with sockets primarily designed for network-oriented data exchange while named pipes focus on local, file-like streaming. Sockets, including TCP for reliable stream-oriented communication and UDP for datagrams, operate using a client-server model involving bind, listen, and accept operations to establish connections, enabling communication across machines or locally via implementations like UNIX domain sockets. In contrast, named pipes are inherently local to a single system, providing a unidirectional channel that appears as a special file in the filesystem, allowing processes to read and write byte streams in a simpler, sequential manner without the overhead of network protocol stacks.
Key differences arise in scope, directionality, and features: named pipes are restricted to intra-system use and typically unidirectional, requiring two pipes for bidirectional flow, whereas sockets support cross-machine communication, native bidirectionality, and advanced options like non-blocking I/O and message boundaries for structured data.[2] On Unix-like systems, named pipes (implemented as FIFOs) offer basic streaming but lack the socket abstractions for datagrams or sequenced packets, while UNIX domain sockets provide these capabilities locally without traversing the network layer, making them more versatile for complex local interactions.[42] In Windows, named pipes can extend to networked use via the Server service but perform best locally, differing from sockets' optimized handling of remote, scalable connections.[2]
Performance-wise, named pipes generally exhibit lower latency for local byte stream transfers compared to sockets, as they bypass network abstractions; benchmarks on Linux show named pipes achieving approximately 30% higher throughput than UNIX domain sockets for small message sizes (e.g., 100 bytes), though the gap narrows for larger payloads where sockets may edge out due to efficient buffering.[33] Sockets, particularly TCP variants, incur additional overhead from protocol processing, making them less ideal for pure local IPC but superior for scenarios involving structured messages or high concurrency across distributed systems.[43]
Named pipes are preferable for low-overhead, intra-system communication where simplicity and minimal latency suffice, such as streaming data between related processes on the same host. Sockets shine in distributed environments requiring portability, scalability, or network traversal, like client-server applications spanning multiple machines. Historically, UNIX domain sockets emerged as a flexible alternative to named pipes for local IPC, offering socket-like semantics without network involvement and influencing modern implementations in systems like Windows Subsystem for Linux.[44]