Mastering eBPF: Install, Write Hello World, and Explore Advanced Examples
This article guides you through installing BCC, creating simple and complex eBPF programs with C and Python, explains eBPF architecture, source‑code organization, and demonstrates real‑world tracing examples for kernel diagnostics and performance monitoring.
Introduction
eBPF is increasingly used for daily problem diagnosis and has become mature. It can locate kernel issues and optimize network performance, especially in scenarios where server restarts are not allowed. eBPF programs are write‑once and can be executed directly, offering high efficiency.
1. How to Write eBPF
1.1 BCC Tool Installation
Install the BCC tools on a Linux system with the following commands:
<code>sudo yum install bcc kernel-headers-$(uname -r)</code>If the above fails, use:
<code>sudo yum install bcc-tools</code>After installation, verify with
bcc -v. For detailed instructions see the official install guide .
1.2 Hello World Program
eBPF programs consist of two parts: a kernel‑space C file and a user‑space Python loader.
Example files
hello.cand
hello.py:
hello.c
<code>#include <uapi/linux/ptrace.h>
int hello_world(struct pt_regs *regs, int dfd, const char __user *filename, int flags, umode_t mode) {
bpf_trace_printk("Hello, World!:%d\n", dfd);
bpf_trace_printk("Hello, World!:%s\n", filename);
return 0;
}</code>hello.py
<code>#!/usr/bin/env python3
from bcc import BPF
# Load the eBPF C program
b = BPF(src_file="hello.c")
# Attach the program to the do_sys_open kprobe
b.attach_kprobe(event="do_sys_open", fn_name="hello_world")
# Print output from the kernel program
b.trace_print()</code>Run the program with
python3 hello.pyto see the output.
1.3 Example Development
After installing BCC, the directory
/usr/share/bcc/toolscontains ready‑made eBPF tools that can be examined and modified.
Example 1: syncsnoop – Traces the
syncsystem call using a kprobe. The left pane shows the C code loaded into the kernel, and the right pane shows the Python driver.
Example 2: vfscount – Demonstrates a Python loader that creates a hash table to count calls to VFS functions. The kernel part uses a tracepoint to record the data.
Example 3: hardirqs – Uses a tracepoint to monitor hardware interrupts, storing results in a hash table.
2. eBPF Limitations
3. eBPF Architecture
eBPF consists of a user‑space component and a kernel‑space component.
The user‑space part loads BPF bytecode into the kernel and reads statistics or event details returned from the kernel.
The kernel‑space bytecode runs inside the kernel, handling specific events and optionally sending results back to user space via maps or perf‑event mechanisms.
Interaction between user and kernel space is achieved through map structures, enabling bidirectional communication.
Typical workflow:
Compile the BPF program to bytecode using LLVM or GCC.
Load the bytecode into the kernel with a loader program.
The kernel verifier checks safety before execution.
Running BPF programs can return data via maps (for aggregated statistics) or perf‑events (for real‑time events).
4. eBPF Source Code
4.1 Directory Organization
User‑space loading
ELF parsing source:
tkernel4/samples/bpf/Libbpf provides helper functions such as
bpf_load_programand ultimately invokes the
__NR_bpfsystem call.
Kernel‑space
Source directory:
tkernel4/kernel/bpf/ syscall.cimplements the
bpf()system call for loading and verification.
core.cselects the runtime (interpreter or JIT).
verifier.ccontains
bpf_check()for safety validation.
4.2 Source Code Analysis
The user‑space side includes eBPF tool functions and libraries, while the kernel side provides the eBPF runtime, kprobe mechanism, ftrace, and tracepoints.
Function IDs (
func_id) are defined in
include/uapi/linux/bpf.husing the
BPF_FUNC_*macros:
<code>#define __BPF_ENUM_FN(x) BPF_FUNC_ ## x
enum bpf_func_id {
__BPF_FUNC_MAPPER(__BPF_ENUM_FN)
__BPF_FUNC_MAX_ID,
};</code>These IDs map to kernel implementations, e.g.,
BPF_FUNC_msg_redirect_hash. The kernel’s
net/core/filter.cregisters a verifier ops structure that resolves a
func_idto a
bpf_func_proto:
<code>const struct bpf_verifier_ops sk_msg_verifier_ops = {
.get_func_proto = sk_msg_func_proto,
.is_valid_access = sk_msg_is_valid_access,
.convert_ctx_access = sk_msg_convert_ctx_access,
.gen_prologue = bpf_noop_prologue,
};</code>During loading,
kernel/bpf/verifier.ccalls
fixup_bpf_callsto replace the
func_idimmediate value with the actual function pointer offset:
<code>fn = env->ops->get_func_proto(insn->imm, env->prog);
insn->imm = fn->func - __bpf_call_base;</code>When the kernel runs the eBPF program, the dispatcher executes the resolved function:
<code>BPF_R0 = (__bpf_call_base + insn->imm)(BPF_R1, BPF_R2, BPF_R3, BPF_R4, BPF_R5);</code>This article shares eBPF tool installation methods, analyzes the composition of user‑space tool core files, and explains the kernel’s support mechanisms for eBPF.
Tencent Architect
We share technical insights on storage, computing, and access, and explore industry-leading product technologies together.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.