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 = https://www.cnblogs.com/senberhu/archive/2021/12/27/{}; //創建堆疊上結構體,第一次記憶體拷貝
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 = https://www.cnblogs.com/senberhu/archive/2021/12/27/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 = https://www.cnblogs.com/senberhu/archive/2021/12/27/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中拷貝結構體的操作,
差異性
總結:
共同點:
-
Perf/Ring Buffer相對于其他種類map(被動輪詢)來說,提供專用api,通知應用層事件就緒,減少cpu消耗,提高性能,
-
采用共享記憶體,節省復制資料開銷,
-
Perf/Ring Buffer支持傳入可變長結構,
差異:
-
Perf Buffer每個CPU核心一個快取區,不保證資料順序(fork exec exit),會對我們應用層消費資料造成影響,Ring Buffer多CPU共用一個快取區且內部實作了自旋鎖,保證資料順序,
-
Perf Buffer有著兩次資料拷貝動作,當空間不足時,效率低下, Ring Buffer采用先申請記憶體,再操作形式,提高效率,
-
Ring Buffer性能強于Perf Buffer,參考patch 【ringbuf perfbuf 性能對比】
本文由博客一文多發平臺 OpenWrite 發布!
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/394977.html
標籤:其他
