经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 其他 » 网络安全 » 查看文章
Perf / Ring Buffer对比
来源:cnblogs  作者:SenberHu  时间:2021/12/31 8:56:24  对本文有异议

Perf Buffer常规用法:

  1. struct addrinfo //需要上传给应用层的数据结构
  2. {
  3. int ai_flags; /* Input flags. */
  4. int ai_family; /* Protocol family for socket. */
  5. int ai_socktype; /* Socket type. */
  6. int ai_protocol; /* Protocol for socket. */
  7. u32 ai_addrlen; /* Length of socket address. */ // CHANGED from socklen_t
  8. struct sockaddr *ai_addr; /* Socket address for socket. */
  9. char *ai_canonname; /* Canonical name for service location. */
  10. struct addrinfo *ai_next; /* Pointer to next in list. */
  11. };
  12. struct //Perf Map声明
  13. {
  14. __uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY);
  15. __uint(key_size, sizeof(int));
  16. __uint(value_size, sizeof(int)); //这里不是 struct addrinfo大小,这里指的是key对应的fd的大小 *****
  17. __uint(max_entries, 1024); //最大 fd 数量,这里可以不设置,在应用层设置,会覆盖这里的值,尽量保证一个cpu对应一个buffer
  18. // https://github.com/cilium/ebpf/pull/300
  19. // https://github.com/cilium/ebpf/issues/209
  20. // https://github.com/cilium/ebpf/blob/02ebf28c2b0cd7c2c6aaf56031bc54f4684c5850/map.go 的函数 clampPerfEventArraySize() 里面
  21. } events SEC(".maps");
  22. SEC("uretprobe/getaddrinfo")
  23. int getaddrinfo_return(struct pt_regs *ctx) {
  24. ...
  25. struct data_t data = {}; //创建栈上结构体,第一次内存拷贝
  26. data.xxx = xxx; //获取需要的数据
  27. bpf_perf_event_output(ctx, &events, BPF_F_CURRENT_CPU, &data, sizeof(data)); //将栈上结构体复制到Perf Map中,第二次内存拷贝
  28. ...
  29. return 0;
  30. }

总结: 在栈中申请的结构体,此时ebpf verify验证器会限制结构体不能超过512字节,影响功能开发。

发生了2次内存拷贝,消耗性能。

  1. 在对结构体成员赋值完成后,调用bpf_perf_event_output时,如果Perf Map已经满了。则会发生上传数据失败。

Perf Buffer高阶用法:

  1. struct addrinfo //需要上传给应用层的数据结构
  2. {
  3. int ai_flags; /* Input flags. */
  4. int ai_family; /* Protocol family for socket. */
  5. int ai_socktype; /* Socket type. */
  6. int ai_protocol; /* Protocol for socket. */
  7. u32 ai_addrlen; /* Length of socket address. */ // CHANGED from socklen_t
  8. struct sockaddr *ai_addr; /* Socket address for socket. */
  9. char *ai_canonname; /* Canonical name for service location. */
  10. struct addrinfo *ai_next; /* Pointer to next in list. */
  11. };
  12. struct { //Perf Map声明
  13. __uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY);
  14. __uint(key_size, sizeof(int));
  15. __uint(value_size, sizeof(int));
  16. __uint(max_entries, 1024);
  17. } events SEC(".maps");
  18. struct { //高阶用法,改为Map堆中创建数据结构
  19. __uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
  20. __uint(max_entries, 1);
  21. __type(key, int);
  22. __type(value, struct event);
  23. } heap SEC(".maps");
  24. SEC("uretprobe/getaddrinfo")
  25. int getaddrinfo_return(struct pt_regs *ctx) {
  26. ...
  27. struct data_t *data; //差异点,不创建栈上数据结构
  28. int zero = 0;
  29. data = bpf_map_lookup_elem(&heap, &zero); //改为创建在Map堆中
  30. data.xxx = xxx; //获取需要的数据
  31. bpf_perf_event_output(ctx, &events, BPF_F_CURRENT_CPU, data, sizeof(*data)); //上传数据
  32. ...
  33. return 0;
  34. }

总结:内存申请发生在Map提供的堆中,规避栈上申请512字节的限制

  1. 还是存在调用bpf_perf_event_output时,如果Perf Map已经满了。则会发生上传数据失败。

Ring Buffer用法

  1. struct addrinfo //需要上传给应用层的数据结构
  2. {
  3. int ai_flags; /* Input flags. */
  4. int ai_family; /* Protocol family for socket. */
  5. int ai_socktype; /* Socket type. */
  6. int ai_protocol; /* Protocol for socket. */
  7. u32 ai_addrlen; /* Length of socket address. */ // CHANGED from socklen_t
  8. struct sockaddr *ai_addr; /* Socket address for socket. */
  9. char *ai_canonname; /* Canonical name for service location. */
  10. struct addrinfo *ai_next; /* Pointer to next in list. */
  11. };
  12. struct { //Ring buffer声明,注意此时max_entries代表的是buffer的大小,和Perf buffer中该字段的含义有所不同
  13. __uint(type, BPF_MAP_TYPE_RINGBUF);
  14. __uint(max_entries, 256 * 1024 /* 256 KB */);
  15. } events SEC(".maps");
  16. SEC("uretprobe/getaddrinfo")
  17. int getaddrinfo_return(struct pt_regs *ctx) {
  18. ...
  19. struct data_t *data; //差异点,不创建栈上数据结构
  20. data = bpf_ringbuf_reserve(&events, sizeof(*data), 0); //直接在ring buffer中申请空间
  21. if (!data)
  22. return 0;
  23. data.xxx = xxx; //获取需要的数据
  24. bpf_ringbuf_submit(data, 0); //上传数据
  25. return 0;
  26. }

总结:函数一开始直接在ring buffer中申请空间,申请失败的话直接就返回了,不会执行后续操作,节省时间,一旦申请成功,即可保证bpf_ringbuf_submit一定不会因为没有空间失败,且省去Perf buffer中拷贝结构体的操作。

差异性

总结:

共同点:

  1. Perf/Ring Buffer相对于其他种类map(被动轮询)来说,提供专用api,通知应用层事件就绪,减少cpu消耗,提高性能。

  2. 采用共享内存,节省复制数据开销。

  3. Perf/Ring Buffer支持传入可变长结构。

差异:

  1. Perf Buffer每个CPU核心一个缓存区,不保证数据顺序(fork exec exit),会对我们应用层消费数据造成影响。Ring Buffer多CPU共用一个缓存区且内部实现了自旋锁,保证数据顺序。

  2. Perf Buffer有着两次数据拷贝动作,当空间不足时,效率低下。 Ring Buffer采用先申请内存,再操作形式,提高效率。

  3. Ring Buffer性能强于Perf Buffer。参考patch 【ringbuf perfbuf 性能对比】

本文由博客一文多发平台 OpenWrite 发布!

原文链接:http://www.cnblogs.com/senberhu/p/15737119.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号