A libbpf-based tool loads and attaches an eBPF program on a Linux host, then the user-space process exits and the program vanishes from `bpftool prog show`. What owns a loaded BPF object's lifetime, and how do you make one outlive the process that loaded it?
answer
- fds, not names
- refcount hits zero at exit
- a directory entry can be a reference
- /sys/fs/bpf is a filesystem, not a directory
- rm is how you unpin
basics
~20 sLoaded BPF programs, maps and links are refcounted kernel objects with no name in any namespace; the loader's file descriptors are usually the only reference, so closing them at process exit frees everything. Pinning to the bpffs filesystem at /sys/fs/bpf takes an extra reference that survives the process.
solid answer
~50 sA BPF program or map is a kernel object referenced by file descriptors, not something registered under a persistent name. When the loader exits, its fds are closed, the refcount drops to zero and the kernel frees the object — that is by design, so a crashed tool cannot leak kernel state. To make one persist you give the kernel another reference. The usual mechanism is **pinning** into the BPF filesystem, a special filesystem mounted at `/sys/fs/bpf` on most systemd distributions: `bpftool prog pin id 42 /sys/fs/bpf/myprog`, or from code `bpf_object__pin()`, `bpf_map__pin()` and `bpf_link__pin()`. The path is the reference; `rm` on it releases it. Note that some attachments hold their own reference — a program attached to a cgroup or a network interface stays alive on that hook — whereas an fd-based tracing attachment dies with the loader unless the `bpf_link` is pinned.
code
bash · 5 linesmount | grep -q '/sys/fs/bpf' || mount -t bpf bpffs /sys/fs/bpf
bpftool prog show
bpftool prog pin id 42 /sys/fs/bpf/myprog
bpftool prog show pinned /sys/fs/bpf/myprog
rm /sys/fs/bpf/myproggo deeper
Know that an eBPF program is loaded by a user-space process and normally disappears when that process exits, and that bpftool prog show lists what is currently loaded.
Explain the refcount model: file descriptors are the references, exit closes them, and pinning under /sys/fs/bpf adds one that outlives the process. Name bpftool prog pin and rm as the two halves.
Show the production judgment — reusing a pinned map so a restarting agent keeps its state, distinguishing a pinned program from a persisted attachment, and tracing orphaned programs on an inherited host back to their pins or hooks.
Own the convention: whether agents on your fleet pin at all, the naming scheme under /sys/fs/bpf, who is responsible for cleanup, and how you avoid kernel memory quietly held by objects nobody can attribute.
## BPF objects are anonymous and refcounted When the `bpf()` syscall loads a program or creates a map, the kernel returns a **file descriptor**. That fd is the handle: there is no global registry you can look a program up in by name, and the numeric IDs shown by `bpftool prog show` are informational, not owning references. The kernel keeps a reference count on each object and frees it — including the map memory, which is charged against the loader's memory accounting — as soon as the count reaches zero. Process exit closes every fd the process held. If those were the only references, the program, its maps and its attachments all disappear at once. This is deliberate and it is a *feature*: a tracing tool that segfaults must not leave kernel programs running forever with no way to find or remove them. ## The three kinds of reference 1. **An open file descriptor** in some process. The default, and the one that dies with the loader. 2. **A pin in bpffs.** The BPF filesystem is a special filesystem type (`mount -t bpf bpffs /sys/fs/bpf`) whose directory entries hold references to BPF objects. Creating one is called pinning; deleting the entry with `rm` unpins. systemd mounts bpffs at `/sys/fs/bpf` on modern distributions, so it is usually already there. 3. **An attachment that the kernel itself owns.** Attaching to a cgroup, or to a network interface hook, records the program in that object, so it survives the loader. This is why a program attached to an interface must be explicitly detached rather than merely having its loader killed — while a kprobe attached through a perf event fd goes away when that fd closes. ## Pinning from the command line ```bash bpftool prog show # find the id bpftool prog pin id 42 /sys/fs/bpf/myprog bpftool map pin id 17 /sys/fs/bpf/mymap bpftool prog show pinned /sys/fs/bpf/myprog rm /sys/fs/bpf/myprog # release the reference ``` A pinned object can be reopened later by a completely different process, which is the standard way to split a tool into a privileged loader and an unprivileged reader: the loader pins the map and exits, the reader opens the pin and polls it. ## Pinning from libbpf - `bpf_object__pin_maps(obj, "/sys/fs/bpf/mytool")` and `bpf_object__pin(obj, path)` pin everything under a directory. - `bpf_map__pin(map, path)` / `bpf_program__pin(prog, path)` pin one object. - `bpf_map__set_pin_path()` before load, or declaring `__uint(pinning, LIBBPF_PIN_BY_NAME)` in the map definition, makes libbpf pin the map automatically under `/sys/fs/bpf/<name>` — and, importantly, **reuse** an existing pin of the same name instead of creating a fresh map. That is how a tool can restart without losing accumulated map contents. - `bpf_link__pin(link, path)` persists the *attachment* for hooks that are otherwise fd-owned. Since the kernel gained `bpf_link` as a first-class object, this is the clean way to keep a tracing attachment alive across a loader restart; `bpftool link pin` does the same from the shell. There is a matching detail on the libbpf side: by default `bpf_link__destroy()` and the skeleton's `__destroy()` detach on exit. If you want persistence you must either pin, or explicitly disown the link so that destroying the user-space handle does not detach the program. ## The operational consequences Persistence cuts both ways. A pinned program is invisible to anyone who does not know to look in `/sys/fs/bpf`, and its maps still consume kernel memory. When you inherit a host and `bpftool prog show` lists programs with no owning process, the pins are where they are anchored — `ls -R /sys/fs/bpf` and `bpftool prog show pinned <path>` will tie them together. Cleanup is filesystem work: remove the pin, and the object goes away once every other reference is gone too. The common interview trap is the opposite direction: an engineer runs a tool, sees the program listed, kills the tool, and is surprised the program is gone. The correct answer is not "a bug" — it is that nothing was holding a reference, and holding one is an explicit act.
- Your loader restarts every few minutes and you need the counters it accumulates to survive the restart. How do you arrange that?Pin the map and reuse the pin rather than creating a new one. Declare the map with `__uint(pinning, LIBBPF_PIN_BY_NAME)` or call `bpf_map__set_pin_path()` before load: libbpf will open the existing pin at `/sys/fs/bpf/<name>` if it is there and create it otherwise. The new process then attaches to the same map object with its existing contents, instead of starting from an empty one.
- You inherit a host where bpftool lists BPF programs but no process seems to own them. How do you work out what they are and remove them?Something else is holding the reference. Walk `/sys/fs/bpf` for pins and match them with `bpftool prog show pinned <path>`; check `bpftool link show` and `bpftool net show` for kernel-held attachments, and `bpftool cgroup tree` for cgroup ones. Remove the pin with `rm` and detach any live attachment; the object is freed once the last reference is gone.
- Is a pinned program still running, or is pinning only a way of keeping it around?Pinning keeps the object alive; it says nothing about whether it is attached to a hook. A pinned but detached program sits in kernel memory doing nothing. Persisting the *attachment* is a separate act — pin the `bpf_link`, or rely on a hook that owns the program itself, such as a cgroup or interface attachment.
saying these in an interview costs you the question
- Thinking programs persist until the machine reboots by default
- Calling /sys/fs/bpf an ordinary directory rather than a filesystem
- Confusing pinning the program with keeping it attached
- Believing a program ID is an owning reference
- Assuming pinned maps cost no kernel memory once the loader exits