Unix Domain Sockets vs Shared Memory
Both Unix Domain Sockets (UDS) and Shared Memory (SHM) are forms of Inter-Process Communication (IPC). However, they represent a fundamental trade-off between Security/Simplicity and Raw Speed.
TL;DR:
- Unix Domain Sockets: Data is generally copied from the sender's memory to a kernel buffer, and then from the kernel buffer to the receiver's memory.
- Shared Memory: Multiple processes map the same physical memory into their own address space. Data is written once and is immediately visible to others with zero-copying.
Unix Domain Sockets (UDS)
A UDS acts like a network socket (TCP/UDP) but exists entirely within the host's kernel. Instead of an IP address, it uses a file path (e.g., /tmp/mysocket).
- How it works: Process A writes data to the socket. The kernel copies that data into a buffer. The kernel then copies that data from the buffer into Process B’s memory.
- The "Double Copy": Because data is copied into and out of the kernel, it is slower than shared memory.
- The "Boundary": It provides a clean "wall" between processes. Process A cannot see Process B's memory.
- The Killer Feature (FD Passing): UDS can "pass" File Descriptors. Process A can open a file and "send" the handle to Process B over the socket. This is exactly how gVisor’s Gofer sends file access to the Sentry.
Shared Memory (SHM)
Shared memory allows two or more processes to map the same physical "page" of RAM into their own address spaces.
- How it works: Process A writes to a memory address. Process B sees that data instantly because it is looking at the exact same physical silicon.
- Zero Copy: There is no copying. It is the fastest possible way to move data.
- The "Synchronization" Problem: Because both processes can touch the memory at the same time, you must use Mutexes or Semaphores to prevent them from overwriting each other. If Process A crashes while holding a lock, Process B might hang forever.
- Security Risk: If Process A is compromised, it can attempt to "fuzz" or corrupt the memory structures that Process B is reading, potentially leading to an exploit.
Comparison Table
| Feature | Unix Domain Sockets (UDS) | Shared Memory (SHM) |
|---|---|---|
| Speed | Moderate (Kernel copies data) | Extremely Fast (Zero copy) |
| Complexity | Easy (Standard read/write) |
Hard (Must handle locks/sync) |
| Isolation | High (Kernel mediates data) | Low (Shared memory space) |
| Descriptor Passing | Yes (Can pass file handles) | No |
| Best For | Control signals, passing files | High-volume data (Video, AI) |
Real-world Examples in gVisor
Example 1: The vDSO (Uses Shared Memory)
The vDSO uses Shared Memory. The kernel writes the current time into a memory page, and every user process maps that page.
- Why? Because we need maximum speed. Checking the time should not require a slow context switch or data copying.
Example 2: The Gofer/9P (Uses UDS)
The Gofer talks to the Sentry via Unix Domain Sockets.
- Why? Because security is the priority. We want a hard boundary between the untrusted sandbox (Sentry) and the file proxy (Gofer). Additionally, the Gofer needs to pass file descriptors to the Sentry, which only UDS can do.
Example 3: LISAFS (The Hybrid)
As gVisor evolved, 9P over UDS was too slow for big data transfers. So, gVisor's new LISAFS uses a hybrid approach:
- It uses UDS for the "Control Path" (e.g., "Open this file," "Give me a handle").
- It creates a Shared Memory region for the "Data Path." When you read a 1GB file, the data stays in shared memory to avoid the "Double Copy" overhead, but the "Open/Close" commands still go through the secure socket.
Summary
- Use Unix Domain Sockets when you want security, simplicity, or need to pass file descriptors.
- Use Shared Memory when you have massive amounts of data and every microsecond counts, and you are willing to handle the complexity of locking.