ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

Perf / Ring Buffer对比

2021-12-27 17:33:12  阅读:173  来源: 互联网

标签:__ Perf Buffer ai int Ring data struct


Perf Buffer常规用法:

struct addrinfo  //需要上传给应用层的数据结构
{
  int ai_flags;         /* Input flags.  */
  int ai_family;        /* Protocol family for socket.  */
  int ai_socktype;      /* Socket type.  */
  int ai_protocol;      /* Protocol for socket.  */
  u32 ai_addrlen;       /* Length of socket address.  */ // CHANGED from socklen_t
  struct sockaddr *ai_addr; /* Socket address for socket.  */
  char *ai_canonname;       /* Canonical name for service location.  */
  struct addrinfo *ai_next; /* Pointer to next in list.  */
};


struct  //Perf Map声明
{
    __uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY);
   	__uint(key_size, sizeof(int));
	  __uint(value_size, sizeof(int)); //这里不是 struct addrinfo大小,这里指的是key对应的fd的大小 *****
    __uint(max_entries, 1024);       //最大 fd 数量,这里可以不设置,在应用层设置,会覆盖这里的值,尽量保证一个cpu对应一个buffer
		// https://github.com/cilium/ebpf/pull/300
    // https://github.com/cilium/ebpf/issues/209
    // https://github.com/cilium/ebpf/blob/02ebf28c2b0cd7c2c6aaf56031bc54f4684c5850/map.go 的函数 clampPerfEventArraySize() 里面
} events SEC(".maps");



SEC("uretprobe/getaddrinfo")
int getaddrinfo_return(struct pt_regs *ctx) {
		...
    struct data_t data = {}; //创建栈上结构体,第一次内存拷贝
		data.xxx = xxx;  //获取需要的数据
    bpf_perf_event_output(ctx, &events, BPF_F_CURRENT_CPU, &data, sizeof(data)); //将栈上结构体复制到Perf Map中,第二次内存拷贝
		...
    return 0;
}

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

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

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

Perf Buffer高阶用法:

struct addrinfo  //需要上传给应用层的数据结构
{
  int ai_flags;         /* Input flags.  */
  int ai_family;        /* Protocol family for socket.  */
  int ai_socktype;      /* Socket type.  */
  int ai_protocol;      /* Protocol for socket.  */
  u32 ai_addrlen;       /* Length of socket address.  */ // CHANGED from socklen_t
  struct sockaddr *ai_addr; /* Socket address for socket.  */
  char *ai_canonname;       /* Canonical name for service location.  */
  struct addrinfo *ai_next; /* Pointer to next in list.  */
};

struct {       //Perf Map声明
	__uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY);
	__uint(key_size, sizeof(int));
	__uint(value_size, sizeof(int));
  __uint(max_entries, 1024);
} events SEC(".maps");

struct {      //高阶用法,改为Map堆中创建数据结构
	__uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);
	__uint(max_entries, 1);
	__type(key, int);
	__type(value, struct event);
} heap SEC(".maps");


SEC("uretprobe/getaddrinfo")
int getaddrinfo_return(struct pt_regs *ctx) {
		...
		struct data_t *data;  //差异点,不创建栈上数据结构
		int zero = 0;
	  data = bpf_map_lookup_elem(&heap, &zero); //改为创建在Map堆中
		data.xxx = xxx;  //获取需要的数据
    bpf_perf_event_output(ctx, &events, BPF_F_CURRENT_CPU, data, sizeof(*data)); //上传数据
		...
    return 0;
}

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

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

Ring Buffer用法

struct addrinfo  //需要上传给应用层的数据结构
{
  int ai_flags;         /* Input flags.  */
  int ai_family;        /* Protocol family for socket.  */
  int ai_socktype;      /* Socket type.  */
  int ai_protocol;      /* Protocol for socket.  */
  u32 ai_addrlen;       /* Length of socket address.  */ // CHANGED from socklen_t
  struct sockaddr *ai_addr; /* Socket address for socket.  */
  char *ai_canonname;       /* Canonical name for service location.  */
  struct addrinfo *ai_next; /* Pointer to next in list.  */
};


struct {    //Ring buffer声明,注意此时max_entries代表的是buffer的大小,和Perf buffer中该字段的含义有所不同
	__uint(type, BPF_MAP_TYPE_RINGBUF);
	__uint(max_entries, 256 * 1024 /* 256 KB */);
} events SEC(".maps");

SEC("uretprobe/getaddrinfo")
int getaddrinfo_return(struct pt_regs *ctx) {
	...
  
	struct data_t *data;  //差异点,不创建栈上数据结构
	data = bpf_ringbuf_reserve(&events, sizeof(*data), 0); //直接在ring buffer中申请空间
	if (!data)
		return 0;

	data.xxx = xxx;  //获取需要的数据
	bpf_ringbuf_submit(data, 0); //上传数据
	return 0;
}

总结:函数一开始直接在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 发布!

标签:__,Perf,Buffer,ai,int,Ring,data,struct
来源: https://www.cnblogs.com/senberhu/p/15737119.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有