日B视频 亚洲,啪啪啪网站一区二区,91色情精品久久,日日噜狠狠色综合久,超碰人妻少妇97在线,999青青视频,亚洲一区二卡,让本一区二区视频,日韩网站推荐

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

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

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

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

Linux閱碼場(chǎng) ? 來(lái)源: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 核心開(kāi)發(fā)者 Andrii Nakryiko 2020 的一篇文章:BPF ring buffer。

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

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

目錄

譯者序1 ringbuf 相比 perfbuf 的改進(jìn) 1.1 降低內(nèi)存開(kāi)銷(xiāo)(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 程序 用戶(hù)空間程序 3.2 ringbuf 示例 內(nèi)核 BPF 程序 用戶(hù)空間程序 3.3 ringbuf reserve/commit API 示例 原理 限制 內(nèi)核 BPF 程序 用戶(hù)空間程序4 ringbuf 事件通知控制 4.1 事件通知開(kāi)銷(xiāo) 4.2 perbuf 解決方式 4.3 ringbuf 解決方式5 總結(jié)其他相關(guān)資料(譯注)

以下是譯文。

很多場(chǎng)景下,BPF 程序都需要將數(shù)據(jù)發(fā)送到用戶(hù)空間(userspace), BPF perf buffer(perfbuf)是目前這一過(guò)程的事實(shí)標(biāo)準(zhǔn),但它存在一些問(wè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 類(lèi)似的 API ,以方便用戶(hù)遷移;又提供了一套新的reserve/commit API(先預(yù)留再提交),以實(shí)現(xiàn)更高性能。

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

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

perfbuf 是 per-CPU 環(huán)形緩沖區(qū)(circular buffers),能實(shí)現(xiàn)高效的 “內(nèi)核-用戶(hù)空間”數(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 來(lái)解決這個(gè)問(wèn)題。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. 通過(guò) memory-mapped region 來(lái)高效地從 userspace 讀數(shù)據(jù),避免內(nèi)存復(fù)制或系統(tǒng)調(diào)用;

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

此外,它還解決了 perfbuf 的下列問(wèn)題:

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

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

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

下面具體來(lái)看。

1.1 降低內(nèi)存開(kāi)銷(xiāo)(memory overhead)

perfbuf 為每個(gè) CPU 分配一個(gè)獨(dú)立的緩沖區(qū),這意味著開(kāi)發(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í)間都比較空閑、周期性來(lái)一大波數(shù)據(jù)的場(chǎng)景, 這個(gè)問(wèn)題尤其突出,很難在兩者之間取得一個(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)景下有一些問(wèn)題:如果這些事件發(fā)生的間隔非常短(幾毫秒)并且分散 在不同 CPU 上,那事件的發(fā)送順序可能就會(huì)亂掉 ——這同樣是 perbuf 的 per-CPU 特性決定的。

舉個(gè)真實(shí)例子,幾年前我寫(xiě)的一個(gè)應(yīng)用需要跟蹤進(jìn)程 fork/exec/exit 事件,收集進(jìn)程級(jí)別(per-process)的資源使用量。BPF 程序?qū)⑦@些事件 寫(xiě)入 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í)行。這里的問(wèn)題很清楚,但要解決這個(gè)問(wèn)題,就需要在應(yīng)用邏輯中加入大量的判斷和處理, 只有親自做過(guò)才知道有多復(fù)雜。

但對(duì)于 ringbuf 來(lái)說(shuō),這根本不是問(wèn)題,因?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ā)送到用戶(hù)空間。這意味著數(shù)據(jù)會(huì)被復(fù)制兩次:

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

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

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

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

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

  • 預(yù)留成功后,

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

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

  • 如果因?yàn)?buffer 沒(méi)有空間而預(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é)果見(jiàn)內(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)。

在編寫(xiě)了一個(gè)真實(shí)場(chǎng)景的高吞吐應(yīng)用之后,我們證實(shí)了 ringbuf 在作為與 perfbuf 類(lèi)似的 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)

完整代碼見(jiàn) bpf-ringbuf-examples project。

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

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

$ 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)體的大小超過(guò) 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 變量的大小超過(guò)了 512 字節(jié),無(wú)法放到 BPF 棧上,// 因此聲明一個(gè) size=1 的 per-CPU array 來(lái)存放 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;}

用戶(hù)空間程序

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

看一個(gè)關(guān)鍵點(diǎn):使用 libbpf user-space perfbuffer_new() API 來(lái)創(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,因此總大小:8 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 的大小(max_entries)可以在 BPF 側(cè)指定了,注意這是所有 CPU 共享的大小。

  • 在 userspace 側(cè)來(lái)設(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()替換成了類(lèi)似的bpf_ringbuf_output(),后者更簡(jiǎn)單,不需要 BPF context 參數(shù)。

用戶(hù)空間程序

事件 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í)也避免了為不需要的功能買(mǎi)單:在實(shí)際中,這功能除了能用戶(hù)在 userspace 打印出有數(shù)據(jù)丟失之外,其他基本也做不了什么, 而類(lèi)似的目的在 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;   }

接下來(lái)基本上就是文本替換一下的事情了: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)

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

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

    如果這一步失敗了(例如由于用戶(hù)空間消費(fèi)不及時(shí)導(dǎo)致 buffer 滿(mǎn)了,或者有大量 突發(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)沒(méi)有可以空間了(返回NULL);

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

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

另外,ring buffer 中預(yù)留的空間在被提交之前,用戶(hù)空間是看不到的, 因此 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ù),用戶(hù)只能退回到用bpf_ringbuf_output()方式來(lái)提交,忍受額外的數(shù)據(jù)復(fù)制開(kāi)銷(xiāo);

  • 其他所有情況下,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; }

用戶(hù)空間程序

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

4 ringbuf 事件通知控制

4.1 事件通知開(kāi)銷(xiāo)

在高吞吐場(chǎng)景中,最大的性能損失經(jīng)常來(lái)自提交數(shù)據(jù)時(shí),內(nèi)核的信號(hào)通知開(kāi)銷(xiā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ā)送一次通知。用戶(hù)空間創(chuàng)建 perfbuf 時(shí)可以指定這個(gè)參數(shù)。

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

4.3 ringbuf 解決方式

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

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

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

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

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

5 總結(jié)

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

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

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

    關(guān)注

    9

    文章

    3238

    瀏覽量

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

    關(guān)注

    117

    文章

    3849

    瀏覽量

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

    關(guān)注

    0

    文章

    26

    瀏覽量

    4753

原文標(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)推薦
    熱點(diǎn)推薦

    Ring Buffer 有什么特別?

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

    BPF2怎么修訂歷史

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

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

    `產(chǎn)品名稱(chēng): 帶通濾波器產(chǎn)品型號(hào):BPF-A580+BPF-A580+ 產(chǎn)品特性線(xià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    英文縮寫(xiě): Resilient Packet Ring 中文譯名: 彈性分組環(huán)
    發(fā)表于 02-23 09:31 ?756次閱讀

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

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

    Ring buffer介紹

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

    保證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)是幾乎可以訪(fǎng)問(wèn)Linux內(nèi)核
    的頭像 發(fā)表于 05-03 11:27 ?2786次閱讀
    保證<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)是幾乎可以訪(fǎng)問(wèn)Linux內(nèi)核和應(yīng)用程序的任何信息,同時(shí),BPF對(duì)系統(tǒng)性能影響很
    的頭像 發(fā)表于 06-30 17:28 ?3216次閱讀
    如何使用<b class='flag-5'>BPF</b>對(duì)Linux內(nèi)核進(jìn)行實(shí)時(shí)跟蹤

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

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

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

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

    Ring Clojure的Web框架

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

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

    本來(lái)想寫(xiě)一篇“BPF 深度分析、環(huán)境搭建與案例分析”的文章,但是篇幅過(guò)長(zhǎng),于是先把BPF編程的環(huán)境搭建先放出來(lái)。接下來(lái)的文章將對(duì)BPF深度分析(包括BPF虛擬機(jī)、
    的頭像 發(fā)表于 10-14 17:02 ?3087次閱讀
    <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、用戶(hù)空間的 BPF 加載器庫(kù)(libbpf)和內(nèi)核中的 BPF 子系統(tǒng)。
    的頭像 發(fā)表于 10-19 11:27 ?1990次閱讀

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

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

    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)用
    卓资县| 宁武县| 济阳县| 遂平县| 饶阳县| 鄂尔多斯市| 湾仔区| 元氏县| 霍州市| 西林县| 绥中县| 丹江口市| 霍林郭勒市| 丹东市| 扶绥县| 水富县| 财经| 武夷山市| 襄樊市| 阆中市| 安岳县| 修文县| 河池市| 新田县| 英德市| 营山县| 灵川县| 韶山市| 三明市| 广灵县| 凤庆县| 吉水县| 太和县| 西盟| 读书| 汨罗市| 治多县| 沽源县| 平度市| 深圳市| 黎平县|