Building and Running an eBPF Application – Part 1
This article walks through creating a first eBPF program using C and Go on Ubuntu 22.04, covering required dependencies, kernel‑space vs user‑space concepts, event selection, BPF map definition, and a tracepoint function that measures per‑process CPU time.
This guide shows how to write a basic eBPF program in C (with a forthcoming Go user‑space component) on Ubuntu 22.04. It starts by installing the Linux headers and the libbpf and bpfcc-tools packages.
What is eBPF?
eBPF is described as a way to extend the Linux kernel with modules without modifying kernel source code; it provides hooks that allow logic to run in kernel space.
User Space vs Kernel Space
Kernel space is a privileged area with full hardware access, while user space runs regular applications with limited permissions.
Selecting an Event
The article recommends the kepler project as a learning example, which tracks CPU time per process by observing the sched_switch tracepoint.
Understanding the Event Format
Running
cat /sys/kernel/debug/tracing/events/sched/sched_switch/formatreveals many fields; the tutorial keeps only the non‑ common_ fields and defines a C structure:
char prev_comm[16];
pid_t prev_pid;
int prev_prio;
long prev_state;
char next_comm[16];
pid_t next_pid;
int next_prio;This leads to the final struct used in the eBPF program:
struct sched_switch_args {
char prev_comm[16];
int prev_pid;
int prev_prio;
long prev_state;
char next_comm[16];
int next_pid;
int next_prio;
};Creating a BPF Map
Three structures are defined: a key struct holding the PID, a value struct storing start and elapsed times, and the map definition linking them:
struct key_t { __u32 pid; };
struct val_t { __u64 start_time; __u64 elapsed_time; };
struct {
__uint(type, BPF_MAP_TYPE_HASH);
__type(key, struct key_t);
__type(value, struct val_t);
__uint(max_entries, 10240);
} process_time_map SEC(".maps");Writing the eBPF Function
The tracepoint program obtains the current timestamp, looks up the previous PID in the map, computes elapsed time, updates the map, and handles the next PID similarly. Key code snippets are:
SEC("tracepoint/sched/sched_switch")
int cpu_processing_time(struct sched_switch_args *ctx) {
__u64 ts = bpf_ktime_get_ns();
struct key_t prev_key = {.pid = ctx->prev_pid};
struct val_t *val = bpf_map_lookup_elem(&process_time_map, &prev_key);
if (val) {
__u64 elapsed = ts - val->start_time;
struct val_t new_val = {.start_time = ts, .elapsed_time = elapsed};
bpf_map_update_elem(&process_time_map, &prev_key, &new_val, BPF_ANY);
return 0;
}
struct key_t next_key = {.pid = ctx->next_pid};
struct val_t *next_val = bpf_map_lookup_elem(&process_time_map, &prev_key);
if (!next_val) {
struct val_t next_new = {.start_time = ts};
bpf_map_update_elem(&process_time_map, &next_key, &next_new, BPF_ANY);
return 0;
}
return 0;
}A license declaration is required for loading the program into the kernel:
char _license[] SEC("license") = "Dual MIT/GPL";Next Steps
The article concludes that the kernel‑side program is complete and hints that the next part will cover a Go user‑space application using the bpf2go tool.
References
what‑is‑ebpf: https://ebpf.io/what-is-ebpf/#what-is-ebpf
kepler GitHub: https://github.com/sustainable-computing-io/kepler/tree/main
CPU scheduling overview: https://www.studytonight.com/operating-system/cpu-scheduling
glibc pid_t documentation: https://ftp.gnu.org/old-gnu/Manuals/glibc-2.2.3/html_node/libc_554.html
bpf2go tool: https://pkg.go.dev/github.com/cilium/ebpf/cmd/bpf2go
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
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.
