0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

BPF ring buffer解決的問題及背后的設(shè)計(jì)

Linux閱碼場(chǎng) ? 來源:Linux閱碼場(chǎng) ? 作者:趙亞楠 ? 2022-05-17 09:37 ? 次閱讀

作者簡(jiǎn)介

趙亞楠,攜程資深架構(gòu)師,負(fù)責(zé)攜程云平臺(tái)網(wǎng)絡(luò)虛擬化、云原生安全、內(nèi)核等基礎(chǔ)設(shè)施研發(fā)工作。

譯者序本文翻譯自 BPF 核心開發(fā)者 Andrii Nakryiko 2020 的一篇文章:BPF ring buffer。

文章介紹了 BPF ring buffer 解決的問題及背后的設(shè)計(jì),并給出了一些代碼示例和內(nèi)核 patch 鏈接,深度和廣度兼?zhèn)洌菍W(xué)習(xí) ring buffer 的極佳參考。

由于譯者水平有限,本文不免存在遺漏或錯(cuò)誤之處。如有疑問,請(qǐng)查閱原文。

目錄

譯者序1 ringbuf 相比 perfbuf 的改進(jìn) 1.1 降低內(nèi)存開銷(memory overhead) 1.2 保證事件順序(event ordering) 1.3 減少數(shù)據(jù)復(fù)制(wasted data copy)2 ringbuf 使用場(chǎng)景和性能 2.1 常規(guī)場(chǎng)景 2.2 高吞吐場(chǎng)景 2.3 不可掩碼中斷(non-maskable interrupt)場(chǎng)景 2.4 小結(jié)3 示例程序(show me the code) 3.1 perfbuf 示例 內(nèi)核 BPF 程序 用戶空間程序 3.2 ringbuf 示例 內(nèi)核 BPF 程序 用戶空間程序 3.3 ringbuf reserve/commit API 示例 原理 限制 內(nèi)核 BPF 程序 用戶空間程序4 ringbuf 事件通知控制 4.1 事件通知開銷 4.2 perbuf 解決方式 4.3 ringbuf 解決方式5 總結(jié)其他相關(guān)資料(譯注)

以下是譯文。

很多場(chǎng)景下,BPF 程序都需要將數(shù)據(jù)發(fā)送到用戶空間(userspace), BPF perf buffer(perfbuf)是目前這一過程的事實(shí)標(biāo)準(zhǔn),但它存在一些問題,例如 浪費(fèi)內(nèi)存(因?yàn)槠?per-CPU 設(shè)計(jì))、事件順序無(wú)法保證等。

作為改進(jìn),內(nèi)核 5.8 引入另一個(gè)新的 BPF 數(shù)據(jù)結(jié)構(gòu):BPF ring buffer(環(huán)形緩沖區(qū),ringbuf),

  • 相比 perf buffer,它內(nèi)存效率更高、保證事件順序,性能也不輸前者;

  • 在使用上,既提供了與 perf buffer 類似的 API ,以方便用戶遷移;又提供了一套新的reserve/commit API(先預(yù)留再提交),以實(shí)現(xiàn)更高性能

此外,實(shí)驗(yàn)與真實(shí)環(huán)境的壓測(cè)結(jié)果都表明,從 BPF 程序發(fā)送數(shù)據(jù)給用戶空間時(shí), 應(yīng)該首選 BPF ring buffer。

1.ringbuf 相比perfbuf的改進(jìn)

perfbuf 是 per-CPU 環(huán)形緩沖區(qū)(circular buffers),能實(shí)現(xiàn)高效的 “內(nèi)核-用戶空間”數(shù)據(jù)交互,在實(shí)際中也非常有用,但 per-CPU 的設(shè)計(jì) 導(dǎo)致兩個(gè)嚴(yán)重缺陷:

  • 內(nèi)存使用效率低下(inefficient use of memory)

  • 事件順序無(wú)法保證(event re-ordering)

因此內(nèi)核 5.8 引入了 ringbuf 來解決這個(gè)問題。ringbuf 是一個(gè)“多生產(chǎn)者、單消費(fèi)者”(multi-producer, single-consumer,MPSC) 隊(duì)列,可安全地在多個(gè) CPU 之間共享和操作。perfbuf 支持的一些功能它都支持,包括,

  1. 可變長(zhǎng)數(shù)據(jù)(variable-length data records);

  2. 通過 memory-mapped region 來高效地從 userspace 讀數(shù)據(jù),避免內(nèi)存復(fù)制或系統(tǒng)調(diào)用;

  3. 支持 epoll notifications 和 busy-loop 兩種獲取數(shù)據(jù)方式。

此外,它還解決了 perfbuf 的下列問題:

  1. 可變長(zhǎng)數(shù)據(jù)(variable-length data records);

  2. 通過 memory-mapped region 來高效地從 userspace 讀數(shù)據(jù),避免內(nèi)存復(fù)制或系統(tǒng)調(diào)用;

  3. 支持 epoll notifications 和 busy-loop 兩種獲取數(shù)據(jù)方式。

下面具體來看。

1.1 降低內(nèi)存開銷(memory overhead)

perfbuf 為每個(gè) CPU 分配一個(gè)獨(dú)立的緩沖區(qū),這意味著開發(fā)者通常需要 在內(nèi)存效率和數(shù)據(jù)丟失之間做出折中:

  1. 越大的 per-CPU buffer 越能避免丟數(shù)據(jù),但也意味著大部分時(shí)間里,大部分內(nèi)存都是浪費(fèi)的;

  2. 盡量小的 per-CPU buffer 能提高內(nèi)存使用效率,但在數(shù)據(jù)量陡增(毛刺)時(shí)將導(dǎo)致丟數(shù)據(jù)。

對(duì)于那些大部分時(shí)間都比較空閑、周期性來一大波數(shù)據(jù)的場(chǎng)景, 這個(gè)問題尤其突出,很難在兩者之間取得一個(gè)很好的平衡。

ringbuf 的解決方式是分配一個(gè)所有 CPU 共享的大緩沖區(qū),

  • “大緩沖區(qū)”意味著能更好地容忍數(shù)據(jù)量毛刺

  • “共享”則意味著內(nèi)存使用效率更高

另外,ringbuf 內(nèi)存效率的擴(kuò)展性也更好,比如 CPU 數(shù)量從 16 增加到 32 時(shí),

  • perfbuf 的總 buffer 會(huì)跟著翻倍,因?yàn)樗?per-CPU buffer;

  • ringbuf 的總 buffer 不一定需要翻倍,就足以處理擴(kuò)容之后的數(shù)據(jù)量。

1.2 保證事件順序(event ordering)

如果 BPF 應(yīng)用要跟蹤一系列關(guān)聯(lián)事件(correlated events),例如進(jìn)程的啟動(dòng)和終止、 網(wǎng)絡(luò)連接的生命周期事件等,那保持事件的順序就非常關(guān)鍵。perfbuf 在這種場(chǎng)景下有一些問題:如果這些事件發(fā)生的間隔非常短(幾毫秒)并且分散 在不同 CPU 上,那事件的發(fā)送順序可能就會(huì)亂掉 ——這同樣是 perbuf 的 per-CPU 特性決定的。

舉個(gè)真實(shí)例子,幾年前我寫的一個(gè)應(yīng)用需要跟蹤進(jìn)程 fork/exec/exit 事件,收集進(jìn)程級(jí)別(per-process)的資源使用量。BPF 程序?qū)⑦@些事件 寫入 perfbuf,但它們到達(dá)的順序經(jīng)常亂掉。這是因?yàn)閮?nèi)核調(diào)度器在不同 CPU 上調(diào)度進(jìn)程時(shí), 對(duì)于那些存活時(shí)間很短的進(jìn)程,fork(), exec(), and exit() 會(huì)在極短的時(shí)間內(nèi)在不同 CPU 上執(zhí)行。這里的問題很清楚,但要解決這個(gè)問題,就需要在應(yīng)用邏輯中加入大量的判斷和處理, 只有親自做過才知道有多復(fù)雜。

但對(duì)于 ringbuf 來說,這根本不是問題,因?yàn)樗枪蚕淼耐粋€(gè)緩沖區(qū)。ringbuf 保證 如果事件 A 發(fā)生在事件 B 之前,那 A 一定會(huì)先于 B 被提交,也會(huì)在 B 之前被消費(fèi)。這個(gè)特性顯著簡(jiǎn)化了應(yīng)用處理邏輯。

1.3 減少數(shù)據(jù)復(fù)制(wasted data copy)

BPF 程序使用 perfbuf 時(shí),必須先初始化一份事件數(shù)據(jù),然后將它復(fù)制到 perfbuf, 然后才能發(fā)送到用戶空間。這意味著數(shù)據(jù)會(huì)被復(fù)制兩次:

  • 第一次:復(fù)制到一個(gè)局部變量(a local variable)或 per-CPU array (BPF 的??臻g很小,因此較大的變量無(wú)法放到棧上,后面有例子)中;

  • 第二次:復(fù)制到perfbuf中。

更糟糕的是,如果 perfbuf 已經(jīng)沒有足夠空間放數(shù)據(jù)了,那第一步的復(fù)制完全是浪費(fèi)的。

BPF ringbuf 提供了一個(gè)可選的 reservation/submit API 來避免這種問題。

  • 首先申請(qǐng)為數(shù)據(jù)預(yù)留空間reserve the space),

  • 預(yù)留成功后,

    • 應(yīng)用就可以直接將準(zhǔn)備發(fā)送的數(shù)據(jù)放到 ringbuf 了,從而節(jié)省了 perfbuf 中的第一次復(fù)制,

    • 將數(shù)據(jù)提交到用戶空間將是一件極其高效、不會(huì)失敗的操作,也不涉及任何額外的內(nèi)存復(fù)制。

  • 如果因?yàn)?buffer 沒有空間而預(yù)留失敗了,那 BPF 程序馬上就能知道,從而也不用再 執(zhí)行 perfbuf 中的第一步復(fù)制。

后面會(huì)有具體例子。

2 ringbuf 使用場(chǎng)景和性能

2.1 常規(guī)場(chǎng)景

對(duì)于所有實(shí)際場(chǎng)景(尤其是那些基于bcc/libbpf 的默認(rèn)配置在使用 perfbuf 的場(chǎng)景), ringbuf 的性能都優(yōu)于 perfbuf 性能。各種不同場(chǎng)景的仿真壓測(cè)(synthetic benchmarking) 結(jié)果見內(nèi)核 patch。

2.2 高吞吐場(chǎng)景

Per-CPU buffer 特性的 perfbuf 在理論上能支持更高的數(shù)據(jù)吞吐, 但這只有在每秒百萬(wàn)級(jí)事件(millions of events per second)的場(chǎng)景下才會(huì)顯現(xiàn)。

在編寫了一個(gè)真實(shí)場(chǎng)景的高吞吐應(yīng)用之后,我們證實(shí)了 ringbuf 在作為與 perfbuf 類似的 per-CPU buffer 使用時(shí),仍然可以作為 perfbuf 的一個(gè)高性能替代品,尤其是用到手動(dòng)管理事件通知(manual data availability notification)機(jī)制時(shí)。

  • BPF side

  • user-space side

2.3 不可掩碼中斷(non-maskable interrupt)場(chǎng)景

唯一需要注意、最好先試驗(yàn)一下的場(chǎng)景:BPF 程序必須在 NMI (non-maskable interrupt) context 中執(zhí)行時(shí),例如處理 cpu-cycles 等 perf events 時(shí)。

ringbuf 內(nèi)部使用了一個(gè)非常輕量級(jí)的 spin-lock,這意味著如果 NMI context 中有競(jìng)爭(zhēng),data reservation 可能會(huì)失敗。因此,在 NMI context 中,如果 CPU 競(jìng)爭(zhēng)非常嚴(yán)重,可能會(huì) 導(dǎo)致丟數(shù)據(jù),雖然此時(shí) ringbuf 仍然有可用空間。

2.4 小結(jié)

除了 NMI context 之外,在其他所有場(chǎng)景中優(yōu)先選擇 ringbuf 而不是 perfbuf 都是非常明智的。

3 示例程序(show me the code)

完整代碼見 bpf-ringbuf-examples project

BPF 程序的功能是 trace 所有進(jìn)程的 exec() 操作,也就是創(chuàng)建新進(jìn)程事件。

每次 exec() 事件:收集進(jìn)程 ID (pid)、進(jìn)程名字 (comm)、可執(zhí)行文件路徑 (filename),然后發(fā)送給用戶空間程序;用戶空間簡(jiǎn)單通過 printf() 打印輸出。用三種不同方式實(shí)現(xiàn),輸出都類似:

$ sudo ./ringbuf-reserve-commit    # or ./ringbuf-output, or ./perfbuf-outputTIME     EVENT PID     COMM             FILENAME1939 EXEC  3232062 sh               /bin/sh1939 EXEC  3232062 timeout          /usr/bin/timeout1939 EXEC  3232063 ipmitool         /usr/bin/ipmitool1939 EXEC  3232065 env              /usr/bin/env1939 EXEC  3232066 env              /usr/bin/env1939 EXEC  3232065 timeout          /bin/timeout1939 EXEC  3232066 timeout          /bin/timeout1939 EXEC  3232067 sh               /bin/sh1939 EXEC  3232068 sh               /bin/sh^C

事件的結(jié)構(gòu)體定義:

#define TASK_COMM_LEN 16#define MAX_FILENAME_LEN 512
// BPF 程序發(fā)送給 userspace 的事件struct event {    int pid;    char comm[TASK_COMM_LEN];    char filename[MAX_FILENAME_LEN];};

這里有意讓這個(gè)結(jié)構(gòu)體的大小超過 512 字節(jié),這樣 event 變量就無(wú)法 放到 BPF ??臻g(max 512Byte)上,后面會(huì)看到 perfbuf 和 ringbuf 程序分別怎么處理。

3.1 perfbuf 示例

內(nèi)核 BPF 程序

// 聲明一個(gè) perfbuf map。幾點(diǎn)注意:// 1. 不用特意設(shè)置 max_entries,libbpf 會(huì)自動(dòng)將其設(shè)置為 CPU 數(shù)量;// 2. 這個(gè) map 的 per-CPU buffer 大小是 userspace 設(shè)置的,后面會(huì)看到struct {  __uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY); // perf buffer (array)  __uint(key_size, sizeof(int));  __uint(value_size, sizeof(int));} pb SEC(".maps");
// 一個(gè) struct event 變量的大小超過了 512 字節(jié),無(wú)法放到 BPF 棧上,// 因此聲明一個(gè) size=1 的 per-CPU array 來存放 event 變量struct {  __uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);    // per-cpu array  __uint(max_entries, 1);  __type(key, int);  __type(value, struct event);} heap SEC(".maps");
SEC("tp/sched/sched_process_exec")int handle_exec(struct trace_event_raw_sched_process_exec *ctx){  unsigned fname_off = ctx->__data_loc_filename & 0xFFFF;  struct event *e;  int zero = 0;
  e = bpf_map_lookup_elem(&heap, &zero);  if (!e) /* can't happen */    return 0;
  e->pid = bpf_get_current_pid_tgid() >> 32;  bpf_get_current_comm(&e->comm, sizeof(e->comm));  bpf_probe_read_str(&e->filename, sizeof(e->filename), (void *)ctx + fname_off);
  // 發(fā)送事件,參數(shù)列表   bpf_perf_event_output(ctx, &pb, BPF_F_CURRENT_CPU, e, sizeof(*e));  return 0;}

用戶空間程序

完整代碼 the user-space side, 基于 BPF skeleton(更多信息見 這里)。

看一個(gè)關(guān)鍵點(diǎn):使用 libbpf user-space perfbuffer_new() API 來創(chuàng)建一個(gè) perf buffer consumer:

  struct perf_buffer *pb = NULL;  struct perf_buffer_opts pb_opts = {};  struct perfbuf_output_bpf *skel;
  /* Set up ring buffer polling */  pb_opts.sample_cb = handle_event;  pb = perf_buffer__new(bpf_map__fd(skel->maps.pb), 8 /* 32KB per CPU */, &pb_opts);

這里設(shè)置 per-CPU buffer 為 32KB, 注意其中的 8 表示的是 number of memory pages,每個(gè) page 是 4KB,因此總大?。? pages x 4096 byte/page = 32KB。

3.2 ringbuf 示例

完整代碼:

  • BPF-side code

  • user-space code

內(nèi)核 BPF 程序

bpf_ringbuf_output()在設(shè)計(jì)上遵循了bpf_perf_event_output()的語(yǔ)義, 以使應(yīng)用從 perfbuf 遷移到 ringbuf 時(shí)更容易。為了看出二者有多相似,這里展示下 兩個(gè)示例代碼的 diff。

--- src/perfbuf-output.bpf.c  2020-10-25 1822.247019800 -0700+++ src/ringbuf-output.bpf.c  2020-10-25 1814.510630322 -0700@@ -6,12 +6,11 @@
 char LICENSE[] SEC("license") = "Dual BSD/GPL";
-/* BPF perfbuf map */+/* BPF ringbuf map */ struct {-  __uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY);-  __uint(key_size, sizeof(int));-  __uint(value_size, sizeof(int));-} pb SEC(".maps");+  __uint(type, BPF_MAP_TYPE_RINGBUF);+  __uint(max_entries, 256 * 1024 /* 256 KB */);+} rb SEC(".maps");
 struct {   __uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);@@ -35,7 +34,7 @@   bpf_get_current_comm(&e->comm, sizeof(e->comm));   bpf_probe_read_str(&e->filename, sizeof(e->filename), (void *)ctx + fname_off);
-  bpf_perf_event_output(ctx, &pb, BPF_F_CURRENT_CPU, e, sizeof(*e));+  bpf_ringbuf_output(&rb, e, sizeof(*e), 0);   return 0; }

只有兩個(gè)小改動(dòng):

  1. ringbuf map 的大?。╩ax_entries)可以在 BPF 側(cè)指定了,注意這是所有 CPU 共享的大小。

  • 在 userspace 側(cè)來設(shè)置(或 override) max_entries 也是可以的,API 是 bpf_map__set_max_entries();

  • max_entries的單位是字節(jié),必須是內(nèi)核頁(yè)大小( 幾乎永遠(yuǎn)是 4096)的倍數(shù),也必須是 2 的冪次

  • bpf_perf_event_output()替換成了類似的bpf_ringbuf_output(),后者更簡(jiǎn)單,不需要 BPF context 參數(shù)。

用戶空間程序

事件 handler 簽名有點(diǎn)變化:

  1. 會(huì)返回錯(cuò)誤信息(進(jìn)而終止 consumer 循環(huán))

  2. 參數(shù)里面去掉了產(chǎn)生這個(gè)事件的 CPU Index

-void handleevent(void *ctx, int cpu, void *data, unsigned int datasz)+int handleevent(void *ctx, void *data, sizet data_sz){const struct event *e = data;struct tm *tm;

如果 CPU index 對(duì)你很重要,那你需要自己在 BPF 代碼中記錄它。

另外,ringbuffer API 不提供丟失數(shù)據(jù)(lost samples)的回調(diào)函數(shù),而 perfbuffer 是支持的。如果需要這個(gè)功能,必須自己在 BPF 代碼中處理。這樣的設(shè)計(jì)對(duì)于一個(gè)(所有 CPU)共享的 ring buffer 能最小化鎖競(jìng)爭(zhēng), 同時(shí)也避免了為不需要的功能買單:在實(shí)際中,這功能除了能用戶在 userspace 打印出有數(shù)據(jù)丟失之外,其他基本也做不了什么, 而類似的目的在 BPF 中可以更顯式和高效地完成。

第二個(gè)不同是 ringbuffer_new() API 更加簡(jiǎn)潔:

  /* Set up ring buffer polling */-  pb_opts.sample_cb = handle_event;-  pb = perf_buffer__new(bpf_map__fd(skel->maps.pb), 8 /* 32KB per CPU */, &pb_opts);-  if (libbpf_get_error(pb)) {+  rb = ring_buffer__new(bpf_map__fd(skel->maps.rb), handle_event, NULL, NULL);+  if (!rb) {     err = -1;-    fprintf(stderr, "Failed to create perf buffer
");+    fprintf(stderr, "Failed to create ring buffer
");     goto cleanup;   }

接下來基本上就是文本替換一下的事情了:perf_buffer__poll()-ring_buffer__poll()

   printf("%-8s %-5s %-7s %-16s %s
",          "TIME", "EVENT", "PID", "COMM", "FILENAME");   while (!exiting) {-    err = perf_buffer__poll(pb, 100 /* timeout, ms */);+    err = ring_buffer__poll(rb, 100 /* timeout, ms */);     /* Ctrl-C will cause -EINTR */     if (err == -EINTR) {       err = 0;       break;     }     if (err < 0) {-      printf("Error polling perf buffer: %d
", err);+      printf("Error polling ring buffer: %d
", err);       break;     }   }

3.3 ringbuf reserve/commit API 示例

bpf_ringbuf_output()API 的目的是確保從 perfbuf 到 ringbuf 遷移時(shí)無(wú)需對(duì) BPF 代 碼做重大改動(dòng),但這也意味著它繼承了 perfbuf API 的一些缺點(diǎn):

  1. 額外的內(nèi)存復(fù)制(extra memory copy)

    這意味著需要額外的空間來構(gòu)建 event 變量,然后將其復(fù)制到 buffer。不僅低效, 而且經(jīng)常需要引入只有一個(gè)元素的 per-CPU array,增加了不必要的處理復(fù)雜性。

  2. 非常晚的 buffer 空間申請(qǐng)(data reservation)

    如果這一步失敗了(例如由于用戶空間消費(fèi)不及時(shí)導(dǎo)致 buffer 滿了,或者有大量 突發(fā)事件導(dǎo)致 buffer 溢出了),那上一步的工作將變得完全無(wú)效,浪費(fèi)內(nèi)存空間和計(jì)算資源。

原理

如果能提前知道事件將在第二步被丟棄,就無(wú)需做第一步了, 節(jié)省一些內(nèi)存和計(jì)算資源,消費(fèi)端反而因此而消費(fèi)地更快一些。但 xxx_output()風(fēng)格的API 是無(wú)法實(shí)現(xiàn)這個(gè)目的的。這就是為什么引入了新的bpfringbufreserve()/bpfringbufcommit() API。

  • 提前預(yù)留空間,或者能立即發(fā)現(xiàn)沒有可以空間了(返回NULL);

  • 預(yù)留成功后,一旦數(shù)據(jù)寫好了,將它發(fā)送到 userspace 是一個(gè)不會(huì)失敗的操作。

    也就是說只要bpf_ringbuf_reserve()返回非空,那隨后的bpf_ringbuf_commit()就永遠(yuǎn)會(huì)成功,因此它沒有返回值。

另外,ring buffer 中預(yù)留的空間在被提交之前,用戶空間是看不到的, 因此 BPF 程序可以從容地組織自己的 event 數(shù)據(jù),不管它有多復(fù)雜、需要多少步驟。這種方式也避免了額外的內(nèi)存復(fù)制和臨時(shí)存儲(chǔ)空間(extra memory copying and temporary storage spaces)。

限制

唯一的限制是:BPF 校驗(yàn)器在校驗(yàn)時(shí)(at verification time), 必須知道預(yù)留數(shù)據(jù)的大小 (size of the reservation),因此不支持動(dòng)態(tài)大小的事件數(shù)據(jù)。

  • 對(duì)于動(dòng)態(tài)大小的數(shù)據(jù),用戶只能退回到用bpf_ringbuf_output()方式來提交,忍受額外的數(shù)據(jù)復(fù)制開銷;

  • 其他所有情況下,reserve/commit API 都應(yīng)該是首選。

內(nèi)核 BPF 程序

  • BPF

  • user-space

--- src/ringbuf-output.bpf.c  2020-10-25 1814.510630322 -0700+++ src/ringbuf-reserve-submit.bpf.c  2020-10-25 1853.409470270 -0700@@ -12,29 +12,21 @@   __uint(max_entries, 256 * 1024 /* 256 KB */); } rb SEC(".maps");
-struct {-  __uint(type, BPF_MAP_TYPE_PERCPU_ARRAY);-  __uint(max_entries, 1);-  __type(key, int);-  __type(value, struct event);-} heap SEC(".maps");- SEC("tp/sched/sched_process_exec") int handle_exec(struct trace_event_raw_sched_process_exec *ctx) {   unsigned fname_off = ctx->__data_loc_filename & 0xFFFF;   struct event *e;-  int zero = 0;
-  e = bpf_map_lookup_elem(&heap, &zero);-  if (!e) /* can't happen */+  e = bpf_ringbuf_reserve(&rb, sizeof(*e), 0);+  if (!e)     return 0;
   e->pid = bpf_get_current_pid_tgid() >> 32;   bpf_get_current_comm(&e->comm, sizeof(e->comm));   bpf_probe_read_str(&e->filename, sizeof(e->filename), (void *)ctx + fname_off);
-  bpf_ringbuf_output(&rb, e, sizeof(*e), 0);+  bpf_ringbuf_submit(e, 0);   return 0; }

用戶空間程序

用戶空間代碼與之前的 ringbuf output API 完全一樣,因?yàn)檫@個(gè) API 涉及到的只是提交方(生產(chǎn)方), 消費(fèi)方還是一樣的方式來消費(fèi)。

4 ringbuf 事件通知控制

4.1 事件通知開銷

在高吞吐場(chǎng)景中,最大的性能損失經(jīng)常來自提交數(shù)據(jù)時(shí),內(nèi)核的信號(hào)通知開銷(in-kernel signalling of data availability) ,也就是內(nèi)核的 poll/epoll 通知阻塞在讀數(shù)據(jù)上的 userspace handler 接收數(shù)據(jù)。

這一點(diǎn)對(duì) perfbuf 和 ringbuf 都是一樣的。

4.2 perbuf 解決方式

perfbuf 處理這種場(chǎng)景的方式是提供了一個(gè)采樣通知(sampled notification)機(jī)制:每 N 個(gè)事件才會(huì)發(fā)送一次通知。用戶空間創(chuàng)建 perfbuf 時(shí)可以指定這個(gè)參數(shù)。

這種機(jī)制能否解決問題,因具體場(chǎng)景而異。

4.3 ringbuf 解決方式

ringbuf 選了一條不同的路:bpfringbufoutput() 和 bpfringbufcommit() 都支持一個(gè)額外的 flags 參數(shù),

  • BPF_RB_NO_WAKEUP:不觸發(fā)通知

  • BPF_RB_FORCE_WAKEUP:會(huì)觸發(fā)通知

基于這個(gè) flags,用戶能實(shí)現(xiàn)更加精確的通知控制。例子見 BPF ringbuf benchmark。

默認(rèn)情況下,如果沒指定任何 flag,ringbuf 會(huì)采用自適應(yīng)通知 (adaptive notification)機(jī)制,根據(jù) userspace 消費(fèi)者是否有滯后(lagging)來動(dòng)態(tài) 調(diào)整通知間隔,盡量確保 userspace 消費(fèi)者既不用承擔(dān)額外開銷,又不丟失任何數(shù)據(jù)。這種默認(rèn)配置在大部分場(chǎng)景下都是有效和安全的,但如果想獲得極致性能,那 顯式控制數(shù)據(jù)通知就是有必要的,需要結(jié)合具體應(yīng)用場(chǎng)景和處理邏輯來設(shè)計(jì)。

5 總結(jié)

本文介紹了 BPF ring buffer 解決的問題及其背后的設(shè)計(jì)。

文中給出的示例代碼和內(nèi)核代碼鏈接,展示了 ringbuf API 的基礎(chǔ)和高級(jí)用法。希望閱讀本文之后,讀者能對(duì) ringbuf 有一個(gè)很好的理解和把握,能根據(jù)自己的具體應(yīng)用 選擇合適的 API 來使用。

其他相關(guān)資料(譯注)

內(nèi)核文檔,BPF ring buffer

有一些更細(xì)節(jié)的設(shè)計(jì)與實(shí)現(xiàn),可作為本文補(bǔ)充。

原文標(biāo)題:[譯] BPF ring buffer:使用場(chǎng)景、核心設(shè)計(jì)及程序示例

文章出處:【微信公眾號(hào):Linux閱碼場(chǎng)】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

審核編輯:湯梓紅
聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • 內(nèi)存
    +關(guān)注

    關(guān)注

    8

    文章

    2902

    瀏覽量

    73536
  • 程序
    +關(guān)注

    關(guān)注

    115

    文章

    3719

    瀏覽量

    80356
  • BPF
    BPF
    +關(guān)注

    關(guān)注

    0

    文章

    24

    瀏覽量

    3926

原文標(biāo)題:[譯] BPF ring buffer:使用場(chǎng)景、核心設(shè)計(jì)及程序示例

文章出處:【微信號(hào):LinuxDev,微信公眾號(hào):Linux閱碼場(chǎng)】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

收藏 人收藏

    評(píng)論

    相關(guān)推薦

    Ring Buffer 有什么特別?

    首先 - Ring Buffer。我對(duì) Disruptor 的最初印象只有 Ring Buffer。后來我漸漸明白 Ring
    發(fā)表于 05-25 00:41

    BPF2怎么修訂歷史

    我剛從設(shè)計(jì)器V4.4切換到V5.3,我注意到BPF2過濾器現(xiàn)在是V6.0。我要去哪里修改歷史? 以上來自于百度翻譯 以下為原文I have just switched from Designer
    發(fā)表于 05-17 12:29

    BPF-A580+帶通濾波器產(chǎn)品介紹

    `產(chǎn)品名稱: 帶通濾波器產(chǎn)品型號(hào):BPF-A580+BPF-A580+ 產(chǎn)品特性線性相位,F(xiàn)c±60 MHz時(shí),典型值高達(dá)±6度高拒絕率屏蔽盒水性廢水工作溫度-40 o C至85 o C儲(chǔ)存溫度
    發(fā)表于 09-24 15:29

    什么是Resilient Packet Ring

    什么是Resilient Packet Ring    英文縮寫: Resilient Packet Ring 中文譯名: 彈性分組環(huán)
    發(fā)表于 02-23 09:31 ?601次閱讀

    粉紅圈(pink ring),粉紅圈(pink ring)是

    粉紅圈(pink ring)定義成因/影響/改善 粉紅圈(pink ring)的定義 板面在氧化后,生成一絨毛層(氧化銅及氧化亞銅)。在本質(zhì)
    發(fā)表于 03-27 16:27 ?2515次閱讀

    Ring buffer介紹

    首先 - Ring Buffer。我對(duì) Disruptor 的最初印象只有 Ring Buffer。后來我漸漸明白 Ring
    發(fā)表于 04-02 14:32 ?3134次閱讀

    保證BPF程序安全的BPF驗(yàn)證器介紹

    1. 前言 我們可以使用BPF對(duì)Linux內(nèi)核進(jìn)行跟蹤,收集我們想要的內(nèi)核數(shù)據(jù),從而對(duì)Linux中的程序進(jìn)行分析和調(diào)試。與其它的跟蹤技術(shù)相比,使用BPF的主要優(yōu)點(diǎn)是幾乎可以訪問Linux內(nèi)核
    的頭像 發(fā)表于 05-03 11:27 ?1771次閱讀
    保證<b class='flag-5'>BPF</b>程序安全的<b class='flag-5'>BPF</b>驗(yàn)證器介紹

    如何使用BPF對(duì)Linux內(nèi)核進(jìn)行實(shí)時(shí)跟蹤

    我們可以使用BPF對(duì)Linux內(nèi)核進(jìn)行跟蹤,收集我們想要的內(nèi)核數(shù)據(jù),從而對(duì)Linux中的程序進(jìn)行分析和調(diào)試。與其它的跟蹤技術(shù)相比,使用BPF的主要優(yōu)點(diǎn)是幾乎可以訪問Linux內(nèi)核和應(yīng)用程序的任何信息,同時(shí),BPF對(duì)系統(tǒng)性能影響很
    的頭像 發(fā)表于 06-30 17:28 ?2167次閱讀
    如何使用<b class='flag-5'>BPF</b>對(duì)Linux內(nèi)核進(jìn)行實(shí)時(shí)跟蹤

    BPF系統(tǒng)調(diào)用與Tracing類型的BPF程序

    既然是提供向內(nèi)核注入代碼的技術(shù),那么安全問題肯定是重中之重。平時(shí)防范他人通過漏洞向內(nèi)核中注入代碼,這下子專門開了一個(gè)口子不是大開方便之門。所以內(nèi)核指定了很多的規(guī)則來限制BPF代碼,確保它的錯(cuò)誤不會(huì)影響到內(nèi)核:
    的頭像 發(fā)表于 03-14 16:42 ?3232次閱讀

    BPF ring buffer解決的問題及其背后的設(shè)計(jì)

    因此內(nèi)核 5.8 引入了 ringbuf 來解決這個(gè)問題。ringbuf 是一個(gè)“多生產(chǎn)者、單消費(fèi)者”(multi-producer, single-consumer,MPSC) 隊(duì)列,可安全地在多個(gè) CPU 之間共享和操作。
    的頭像 發(fā)表于 05-07 11:12 ?1083次閱讀

    Ring Clojure的Web框架

    ./oschina_soft/ring.zip
    發(fā)表于 06-13 09:38 ?1次下載
    <b class='flag-5'>Ring</b> Clojure的Web框架

    BPF編程的環(huán)境搭建方法

    本來想寫一篇“BPF 深度分析、環(huán)境搭建與案例分析”的文章,但是篇幅過長(zhǎng),于是先把BPF編程的環(huán)境搭建先放出來。接下來的文章將對(duì)BPF深度分析(包括BPF虛擬機(jī)、
    的頭像 發(fā)表于 10-14 17:02 ?1884次閱讀
    <b class='flag-5'>BPF</b>編程的環(huán)境搭建方法

    BPF為內(nèi)核編程提供了一個(gè)新的參考模型

    這個(gè)新的編程環(huán)境混合使用了 C語(yǔ)言擴(kuò)展以及運(yùn)行時(shí)環(huán)境的組合實(shí)現(xiàn)的,這個(gè)運(yùn)行時(shí)環(huán)境包含了 Clang、用戶空間的 BPF 加載器庫(kù)(libbpf)和內(nèi)核中的 BPF 子系統(tǒng)。
    的頭像 發(fā)表于 10-19 11:27 ?1032次閱讀

    內(nèi)核觀測(cè)技術(shù)BPF詳解

    BPF簡(jiǎn)介 BPF,全稱是Berkeley Packet Filter(伯克利數(shù)據(jù)包過濾器)的縮寫。其誕生于1992年,最初的目的是提升網(wǎng)絡(luò)包過濾工具的性能。后面,隨著這個(gè)工具重新實(shí)現(xiàn)BPF的內(nèi)核
    的頭像 發(fā)表于 11-10 10:34 ?1036次閱讀

    MSPM0 UART通信中DMA和Ring Buffer環(huán)形緩沖的應(yīng)用

    電子發(fā)燒友網(wǎng)站提供《MSPM0 UART通信中DMA和Ring Buffer環(huán)形緩沖的應(yīng)用.pdf》資料免費(fèi)下載
    發(fā)表于 09-05 11:01 ?0次下載
    MSPM0 UART通信中DMA和<b class='flag-5'>Ring</b> <b class='flag-5'>Buffer</b>環(huán)形緩沖的應(yīng)用