# How bpftime Implements Interprocess eBPF Maps Using Shared Memory

> Discover how bpftime utilizes shared memory for interprocess eBPF maps, enabling zero-copy access across processes for efficient communication without kernel intervention.

- Repository: [eunomia-bpf/bpftime](https://github.com/eunomia-bpf/bpftime)
- Tags: internals
- Published: 2026-03-01

---

**bpftime enables multiple processes to share eBPF maps without kernel involvement by storing map descriptors and data structures in a Boost.Interprocess managed shared memory segment, enabling zero-copy access across processes via offset pointers that remain valid across different address spaces.**

bpftime is a userspace eBPF runtime that allows agents running eBPF programs and daemons managing them to communicate through shared data structures. By implementing interprocess eBPF maps using shared memory, the runtime eliminates syscall overhead and kernel-userspace copying while maintaining compatibility with standard eBPF map APIs.

## Global Shared-Memory Segment Initialization

The foundation for interprocess communication begins with `bpftime_initialize_global_shm()` defined in [`runtime/src/bpftime_shm_internal.cpp`](https://github.com/eunomia-bpf/bpftime/blob/main/runtime/src/bpftime_shm_internal.cpp). This function creates or opens a Boost.Interprocess `managed_shared_memory` segment using placement-new to construct a `bpftime_shm` object directly within the shared region.

The `bpftime_shm` instance maintains a `handler_manager` that stores a registry of all eBPF objects—including programs, maps, and links—within the segment. Because this registry lives in shared memory, any process that attaches to the same named segment (identified by `bpftime_shm::get_global_shm_name()`) can access the identical object handles.

## Map Registration and Lifetime Management

When a user calls `bpftime_maps_create()`, the C shim forwards the request to `bpftime_shm::bpftime_maps_create()`, which ultimately invokes `bpf_map_handler::map_init` in [`runtime/src/handler/map_handler.cpp`](https://github.com/eunomia-bpf/bpftime/blob/main/runtime/src/handler/map_handler.cpp). This method receives a reference to the `managed_shared_memory` object and constructs the concrete map implementation directly within the shared segment.

The initialization uses `memory.construct<impl_type>(container_name.c_str())(...)` to allocate data structures such as `hash_map_impl`, `array_map_impl`, or `ringbuf_map_impl` inside the shared memory. Because Boost.Interprocess allocators manage these objects, they persist independently of the creating process and remain accessible to any process mapping the same segment.

### Reference Counting for Safe Destruction

To manage map lifetime across process boundaries, bpftime allocates a `map_refcount_t` (an `std::atomic_uint32_t`) within the shared segment using `memory.construct<map_refcount_t>(anonymous_instance)(1)`. This atomic counter tracks how many processes currently hold the map open. When a process exits or closes its handle, the counter decrements; only when it reaches zero does the implementation destroy the map data structures within the shared segment.

## Zero-Copy Map Handles with Offset Pointers

The critical mechanism enabling cross-process map access is the use of **Boost.Interprocess offset pointers**. In [`runtime/src/handler/map_handler.hpp`](https://github.com/eunomia-bpf/bpftime/blob/main/runtime/src/handler/map_handler.hpp), the `bpf_map_handler` class stores the map implementation as `boost::interprocess::offset_ptr<void> map_impl_ptr`.

Unlike raw pointers, offset pointers encode the byte offset from the base of the shared memory segment rather than absolute virtual addresses. When Process A creates a hash map and Process B maps the same segment at a different virtual address, the offset pointer still resolves correctly by adding the stored offset to the current process's segment base. This allows processes to share handles regardless of where the shared memory maps in their respective address spaces.

## Map Operations Across Processes

Public C APIs such as `bpftime_map_lookup_elem()`, `bpftime_map_update_elem()`, and `bpftime_map_delete_elem()` delegate to the handler manager, which retrieves the appropriate `bpf_map_handler` and forwards the call to the implementation object stored in shared memory.

Because the underlying containers—whether Boost.Unordered for hash maps or Boost.CircularBuffer for ring buffers—reside in the shared segment, all processes operate directly on the same physical memory. This architecture eliminates data copying between kernel and userspace or between separate processes, providing latency characteristics comparable to in-process data structures while maintaining strict isolation between agents and daemons.

## GPU-Backed Maps (Optional Extension)

For GPU-compatible map implementations, bpftime extends the same shared-memory pattern. In [`runtime/src/bpf_map/gpu/nv_gpu_shared_hash_map.cpp`](https://github.com/eunomia-bpf/bpftime/blob/main/runtime/src/bpf_map/gpu/nv_gpu_shared_hash_map.cpp), the implementation allocates CUDA memory and registers it with `cudaHostRegister` while keeping the map descriptor and metadata within the Boost shared segment.

The `bpf_map_attr::gpu_thread_count` field stores GPU-specific configuration, allowing the agent and daemon to coordinate GPU-accelerated map access through the same shared memory handles used for CPU-backed maps. This enables heterogeneous computing scenarios where eBPF programs trigger GPU kernels while map data remains accessible to CPU-side processes.

## Summary

- bpftime creates a global Boost.Interprocess `managed_shared_memory` segment via `bpftime_initialize_global_shm()` in [`runtime/src/bpftime_shm_internal.cpp`](https://github.com/eunomia-bpf/bpftime/blob/main/runtime/src/bpftime_shm_internal.cpp), establishing the foundation for interprocess eBPF maps using shared memory.
- Map implementations are constructed directly in shared memory using `memory.construct<impl_type>()` within `bpf_map_handler::map_init` ([`runtime/src/handler/map_handler.cpp`](https://github.com/eunomia-bpf/bpftime/blob/main/runtime/src/handler/map_handler.cpp)), ensuring data structures are visible to all processes.
- **Offset pointers** (`boost::interprocess::offset_ptr`) enable cross-process addressing by storing relative offsets rather than absolute virtual addresses, allowing processes to share handles regardless of where the segment maps in their address space.
- Reference counting via `map_refcount_t` (atomic uint32_t in shared memory) ensures safe map destruction when the last process closes its handle.
- All map operations (lookup, update, delete) execute directly on shared memory containers (Boost.Unordered, Boost.CircularBuffer, etc.), providing zero-copy access between agents and daemons.

## Frequently Asked Questions

### How does bpftime ensure that shared memory pointers remain valid when processes map the segment at different virtual addresses?

bpftime uses **Boost.Interprocess offset pointers** (`boost::interprocess::offset_ptr<void>`) stored in the `bpf_map_handler` class. Unlike raw pointers, offset pointers encode the distance from the base of the shared memory segment rather than absolute virtual addresses. When a process maps the shared segment at a different base address, the offset pointer still resolves to the correct location by adding the stored offset to the current process's segment base. This mechanism is defined in [`runtime/src/handler/map_handler.hpp`](https://github.com/eunomia-bpf/bpftime/blob/main/runtime/src/handler/map_handler.hpp) and used throughout [`runtime/src/handler/map_handler.cpp`](https://github.com/eunomia-bpf/bpftime/blob/main/runtime/src/handler/map_handler.cpp) when constructing and accessing map implementations.

### What happens to eBPF maps when the process that created them crashes or exits?

The shared memory segment persists independently of any single process because it is backed by operating system shared memory primitives (POSIX `shm_open` or Windows shared memory objects). When a process exits, the `map_refcount_t` atomic counter—allocated in shared memory via `memory.construct<map_refcount_t>` in [`runtime/src/handler/map_handler.cpp`](https://github.com/eunomia-bpf/bpftime/blob/main/runtime/src/handler/map_handler.cpp)—is decremented. The map data structures remain accessible to other processes as long as the reference count is non-zero. Only when the last process closes the map (or exits, triggering automatic cleanup) does the reference count reach zero, causing destruction of the map implementation within the shared segment. This design ensures fault tolerance and allows daemons to outlive agents.

### Can bpftime share eBPF maps between processes running on different physical machines?

No, bpftime's shared memory implementation relies on **Boost.Interprocess** managed shared memory segments, which are backed by operating system primitives (POSIX `shm_open` or Windows shared memory) that are inherently local to a single machine. The shared memory segment is identified by a name (returned by `bpftime_shm::get_global_shm_name()`) and mapped into each process's virtual address space on the same host. For distributed scenarios across physical machines, bpftime would require a different transport mechanism (such as network-based replication or remote procedure calls) layered on top of the local shared memory architecture. The current implementation in [`runtime/src/bpftime_shm_internal.cpp`](https://github.com/eunomia-bpf/bpftime/blob/main/runtime/src/bpftime_shm_internal.cpp) and [`runtime/src/handler/map_handler.cpp`](https://github.com/eunomia-bpf/bpftime/blob/main/runtime/src/handler/map_handler.cpp) is explicitly designed for single-host interprocess communication.

### How does the performance of bpftime's shared memory maps compare to kernel eBPF maps?

bpftime eliminates the syscall overhead and kernel-user space copying inherent in kernel eBPF maps. In [`runtime/src/handler/map_handler.cpp`](https://github.com/eunomia-bpf/bpftime/blob/main/runtime/src/handler/map_handler.cpp), map operations delegate directly to Boost containers (such as `boost::unordered_map` for hash maps or `boost::circular_buffer` for ring buffers) that reside in the shared memory segment. Because both the agent and daemon map the same physical memory into their address spaces, **zero-copy access** is achieved—reads and writes operate directly on the shared data structures without marshalling through system calls or socket buffers. Benchmarks referenced in the bpftime documentation indicate this architecture can deliver up to 10× higher throughput for observability workloads compared to traditional kernel-based map access, particularly when frequent updates or low-latency lookups are required. The trade-off is that bpftime maps are limited to single-host interprocess communication and do not provide the kernel's security isolation guarantees.