eBPF之ringbuf学习笔记


介绍

BPF的ringbuf被实现为2的指数大小的环形缓冲区,它有两个总是递增的计数器:consumer_pos、producer_pos;

producer_pos是生产者计数器,每当一个record被成功存储,producer_pos就会增加;consumer_pos是消费者计数器,每当在用户空间读取一个record,consumer_pos就会增加;

每一个计数器都存放在独立的页中,因此对用户空间而言,producer_pos是仅可读的,consumer_pos是可读可写的;

相关数据结构

ringbuf

ringbuf的结构比较复杂,从第二张图中可以看到两个计数器被强制对齐在了不同的页中;

ringbuf的mask成员表示环形缓冲区的大小,也就是通过&mask来实现求模,从而实现环形缓冲区的效果;

bpf_ringbuf_map

bpf_ringbuf_hdr

关于这个pg_off:

可以看到pg_off记录了hdr和其ringbuf之间的页面偏移量;

重点函数分析

ringbuf的空间创建

首先分配meta空间和两倍的data空间的物理页:

之后进行虚拟地址空间和物理地址空间之间的映射:

通过上述代码中的pages[nr_data_pages + i] = page可知,将来会有两个虚拟地址映射同一片物理地址:

这样在处理环形的问题的时候会有一定用处。

reserve

reserve用于从ringbuf中预定一片空间,以供后续write使用;

用户态接口如下:(flags必须为0)

主要处理函数如下:

可以看到是从ringbuf的data中分割出一片区域构造一个hdr+content,然后设置hdr的meta值;

最后会返回这样一个空间的指针(其实很像glibc)

submit

函数原型:

关键函数:

这个函数的作用是修改hdr中size的属性位,在eBPF的verifier中会在调用了reserve之后检查后续是否调用了submit(或者discard),如果没有调用则会报错:

BPF_FUNC_ringbuf_reserve 0x83

BPF_FUNC_ringbuf_submit 0x84

用户态借助eBPF写ringbuf

reserve 预留空间

利用eBPF汇编指令STX写入数据

submit提交

mmap接口

对ringbuf的mapfd调用mmap则会走到ringbuf_map_mmap函数,

可以直接使用mmap映射ringbuf的mapfd,然后在用户态直接修改consumer_pos:

expmap_fd = bpf_map_create(BPF_MAP_TYPE_RINGBUF, 0, 0, 0x4000);
if (expmap_fd < 0) perror("BPF_MAP_CREATE");//, err_exit("BPF_MAP_CREATE");
printf("ringbuf_map_fd == %d\n", expmap_fd);
size_t *cons = mmap(0, 0x1000, PROT_READ | PROT_WRITE, MAP_SHARED, expmap_fd, 0);
cons[0] = 0x1000;

调试技巧

在 __bpf_ringbuf_reserve 函数下断点,执行到此后finish,查看返回值rax就是分配的一个ringbuf中的空间,-8就是该chunk的hdr。


文章作者: q1ming
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 q1ming !
  目录