Bootstrap

数据交互

bootstrap本身是个很基础的bpf程序,但也是一个非常经典的bpf程序,根据作者的说法,这个bpf程序是自己写bpf程序的起点,主要原因在于它是一个经典的bpf程序和用户空间进行数据交互的案例。

bpf部分

bpf部分主要设定了两个数据结构,一个是用于存储调用了exec的进程信息的exec_start,另外一个是保存用于与用户空间进行交互的缓冲区ring buffer,这两个数据结构都放在map节中。在使用SEC宏设定插桩点后,宏后面的函数即为对该插桩点的处理函数,libbpf会进行处理给该函数传递系统调用的参数构成的结构体作为参数。接下来就是bpf程序对进程的一些参数进行处理。

exec_start

值得注意的是这一句

1
bpf_map_update_elem(&exec_start, &pid, &ts, BPF_ANY);

这一句更新了exec_start结构中的数据,即添加监视到的进程的pid和调用时间。方便之后使用。

ringbuf部分

该部分核心就是两个函数调用

1
2
3
e = bpf_ringbuf_reserve(&rb, sizeof(*e), 0);
......(event中的数据进行一些处理)
bpf_ringbuf_submit(e, 0);

rb是在之前就申请的缓冲区空间,在这里先用reserve在申请的空间中预定event大小的区域用于存放数据,在放入指定数据后使用submit提交给用户空间。

用户空间

用户空间的处理主要由下面两个函数完成

1
2
3
/* Set up ring buffer polling */
rb = ring_buffer__new(bpf_map__fd(skel->maps.rb), handle_event, NULL, NULL);
err = ring_buffer__poll(rb, 100 /* timeout, ms */);

由注释可知第一句是构建一个ring buffer polling,对于加入该函数对应的修改说明,可以通过查看邮件列表中对应的patch来查看。其中的说明简单概括为poll为当数据可用时回调,而consume则是无论是否有可用数据都进行处理。处理所用的回调函数就是已经预先构造好的handle_event,这个函数的功能就是简单的打印使用了该系统调用的进程信息。

总体来说,流程如下。bpf构造ringbuf->用户空间设定ring buffer polling->bpf监控的监控点有事件发生->将信息放入事件并提交给用户空间->用户空间进行处理