LinuxXLIII · eBPF and Advanced ObservabilityBPF maps
BPF maps and state - communicating with userspace
What you'll learn
- Describe what BPF maps are
- Use the common map types
- Communicate between kernel and userspace
- Recognise map patterns in production tools
Prerequisites
Verified against Ubuntu 24.04 LTS · Debian 12 (Bookworm) · RHEL 9.x · Rocky Linux 9.x · AlmaLinux 9.x · Linux kernel 6.1 LTS / 6.6 LTS · systemd 255+ · OpenSSH 8.7p1 (RHEL 9) / 9.6p1 (Ubuntu 24.04) · nftables 1.0.x · chrony 4.x · Pacemaker 2.1.x · Corosync 3.1.x · 2026-08-09
BPF maps are the key-value data structures that eBPF programs use to store state and communicate with userspace. Without maps, eBPF programs can only do trace-and-print output. With maps, they can do aggregations, histograms, and stateful processing.
Map types
| Type | Description |
|---|---|
BPF_MAP_TYPE_HASH | Hash table (key-value) |
BPF_MAP_TYPE_ARRAY | Fixed-size array |
BPF_MAP_TYPE_LRU_HASH | LRU hash (auto-evict) |
BPF_MAP_TYPE_PERCPU_HASH | Per-CPU hash (no lock contention) |
BPF_MAP_TYPE_RINGBUF | Ring buffer (efficient kernel-to-userspace) |
BPF_MAP_TYPE_PERF_EVENT_ARRAY | Perf event array (legacy) |
For most use cases, BPF_MAP_TYPE_HASH and
BPF_MAP_TYPE_PERCPU_HASH are sufficient. For high-volume
event streaming, BPF_MAP_TYPE_RINGBUF is efficient.
bpftrace and maps
In bpftrace, maps are implicit:
# @ is the map (auto-created)
bpftrace -e 'tracepoint:syscalls:sys_enter_openat { @[comm] = count(); }'
# Per-CPU map
bpftrace -e 'tracepoint:syscalls:sys_enter_openat { @cpu[comm, cpu] = count(); }'
# Histogram
bpftrace -e 'tracepoint:syscalls:sys_enter_read { @bytes = hist(arg2); }'
@[comm] = count() creates a hash map keyed by comm
(process name) with counter values.
Print the map
After running bpftrace, the map is printed when the program ends (or on Ctrl-C):
@:
[
nginx
]
@:
[
12345
]
@:
[
curl
]
Maps in C (bpf)
In raw bpf (C), declare maps explicitly:
struct {
__uint(type, BPF_MAP_TYPE_HASH);
__uint(max_entries, 1024);
__type(key, u32);
__type(value, u64);
} count_map SEC(".maps");
Then access from the program:
u32 key = pid >> 32;
u64 *val = bpf_map_lookup_elem(&count_map, &key);
if (val) {
(*val)++;
}
Communicating with userspace
Maps are the interface between kernel eBPF and userspace. A userspace program reads the map and processes the data:
// userspace
while (1) {
int key = next_key();
u64 value;
bpf_map_lookup_elem(fd, &key, &value);
printf("pid %d: %llu\n", key, value);
}
For events (not aggregates), use BPF_MAP_TYPE_RINGBUF:
// kernel
struct event_t *e = bpf_ringbuf_reserve(&events, sizeof(*e), 0);
*e = ...;
bpf_ringbuf_submit(e, 0);
// userspace
struct event_t *e;
e = bpf_ringbuf_consume(&events);
if (e) {
process(e);
bpf_ringbuf_mark_done(&events, e);
}
Ringbuf is efficient for high-volume event streaming.
Inspect maps with bpftool
# Substitute your own values before running:
KEY_ID=3B4FE6ACC0B21F32
KEY=ABCD1234EF567890
# List maps
bpftool map list
# Dump a map
bpftool map dump id "$KEY_ID"
# Lookup a key
bpftool map lookup id "$KEY_ID" key "$KEY"
# Delete a key
bpftool map delete id "$KEY_ID" key "$KEY"
For production debugging, bpftool map is the standard way
to inspect what an eBPF program is doing.
Knowledge check
Knowledge check · 3 questions
Q1. What is a BPF map?
Q2. BPF ring buffer is efficient for high-volume event streaming.
Q3. Which of the following are valid BPF map types? Select all that apply.
Passing score: 75%. Answers are checked in this browser.