色哟哟视频在线观看-色哟哟视频在线-色哟哟欧美15最新在线-色哟哟免费在线观看-国产l精品国产亚洲区在线观看-国产l精品国产亚洲区久久

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

BPF ring buffer解決的問題及其背后的設計

Linux閱碼場 ? 來源:Linux閱碼場 ? 作者:趙亞楠 ? 2022-05-07 11:12 ? 次閱讀

目錄

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

以下是譯文。

很多場景下,BPF 程序都需要將數據發送到用戶空間(userspace), BPF perf buffer(perfbuf)是目前這一過程的事實標準,但它存在一些問題,例如 浪費內存(因為其 per-CPU 設計)、事件順序無法保證等。

作為改進,內核 5.8 引入另一個新的 BPF 數據結構:BPF ring buffer(環形緩沖區,ringbuf),

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

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

此外,實驗與真實環境的壓測結果都表明,從 BPF 程序發送數據給用戶空間時, 應該首選 BPF ring buffer。

1.ringbuf 相比perfbuf的改進

perfbuf 是 per-CPU 環形緩沖區(circular buffers),能實現高效的 “內核-用戶空間”數據交互,在實際中也非常有用,但 per-CPU 的設計 導致兩個嚴重缺陷:

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

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

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

  1. 可變長數據(variable-length data records);

  2. 通過 memory-mapped region 來高效地從 userspace 讀數據,避免內存復制或系統調用;

  3. 支持 epoll notifications 和 busy-loop 兩種獲取數據方式。

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

  1. 可變長數據(variable-length data records);

  2. 通過 memory-mapped region 來高效地從 userspace 讀數據,避免內存復制或系統調用;

  3. 支持 epoll notifications 和 busy-loop 兩種獲取數據方式。

下面具體來看。

1.1 降低內存開銷(memory overhead)

perfbuf 為每個 CPU 分配一個獨立的緩沖區,這意味著開發者通常需要 在內存效率和數據丟失之間做出折中:

  1. 越大的 per-CPU buffer 越能避免丟數據,但也意味著大部分時間里,大部分內存都是浪費的;

  2. 盡量小的 per-CPU buffer 能提高內存使用效率,但在數據量陡增(毛刺)時將導致丟數據。

對于那些大部分時間都比較空閑、周期性來一大波數據的場景, 這個問題尤其突出,很難在兩者之間取得一個很好的平衡。

ringbuf 的解決方式是分配一個所有 CPU 共享的大緩沖區,

  • “大緩沖區”意味著能更好地容忍數據量毛刺

  • “共享”則意味著內存使用效率更高

另外,ringbuf 內存效率的擴展性也更好,比如 CPU 數量從 16 增加到 32 時,

  • perfbuf 的總 buffer 會跟著翻倍,因為它是 per-CPU buffer;

  • ringbuf 的總 buffer 不一定需要翻倍,就足以處理擴容之后的數據量。

1.2 保證事件順序(event ordering)

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

舉個真實例子,幾年前我寫的一個應用需要跟蹤進程 fork/exec/exit 事件,收集進程級別(per-process)的資源使用量。BPF 程序將這些事件 寫入 perfbuf,但它們到達的順序經常亂掉。這是因為內核調度器在不同 CPU 上調度進程時, 對于那些存活時間很短的進程,fork(), exec(), and exit() 會在極短的時間內在不同 CPU 上執行。這里的問題很清楚,但要解決這個問題,就需要在應用邏輯中加入大量的判斷和處理, 只有親自做過才知道有多復雜。

但對于 ringbuf 來說,這根本不是問題,因為它是共享的同一個緩沖區。ringbuf 保證 如果事件 A 發生在事件 B 之前,那 A 一定會先于 B 被提交,也會在 B 之前被消費。這個特性顯著簡化了應用處理邏輯。

1.3 減少數據復制(wasted data copy)

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

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

  • 第二次:復制到perfbuf中。

更糟糕的是,如果 perfbuf 已經沒有足夠空間放數據了,那第一步的復制完全是浪費的。

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

  • 首先申請為數據預留空間reserve the space),

  • 預留成功后,

    • 應用就可以直接將準備發送的數據放到 ringbuf 了,從而節省了 perfbuf 中的第一次復制

    • 將數據提交到用戶空間將是一件極其高效、不會失敗的操作,也不涉及任何額外的內存復制。

  • 如果因為 buffer 沒有空間而預留失敗了,那 BPF 程序馬上就能知道,從而也不用再 執行 perfbuf 中的第一步復制。

后面會有具體例子。

2 ringbuf 使用場景和性能

2.1 常規場景

對于所有實際場景(尤其是那些基于bcc/libbpf 的默認配置在使用 perfbuf 的場景), ringbuf 的性能都優于 perfbuf 性能。各種不同場景的仿真壓測(synthetic benchmarking) 結果見內核 patch。

2.2 高吞吐場景

Per-CPU buffer 特性的 perfbuf 在理論上能支持更高的數據吞吐, 但這只有在每秒百萬級事件(millions of events per second)的場景下才會顯現。

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

  • BPF side

  • user-space side

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

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

ringbuf 內部使用了一個非常輕量級的 spin-lock,這意味著如果 NMI context 中有競爭,data reservation 可能會失敗。因此,在 NMI context 中,如果 CPU 競爭非常嚴重,可能會 導致丟數據,雖然此時 ringbuf 仍然有可用空間。

2.4 小結

除了 NMI context 之外,在其他所有場景中優先選擇 ringbuf 而不是 perfbuf 都是非常明智的。

3 示例程序(show me the code)

完整代碼見 bpf-ringbuf-examples project

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

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

$ 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

事件的結構體定義:

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

這里有意讓這個結構體的大小超過 512 字節,這樣 event 變量就無法 放到 BPF 棧空間(max 512Byte)上,后面會看到 perfbuf 和 ringbuf 程序分別怎么處理。

3.1 perfbuf 示例

內核 BPF 程序

// 聲明一個 perfbuf map。幾點注意:// 1. 不用特意設置 max_entries,libbpf 會自動將其設置為 CPU 數量;// 2. 這個 map 的 per-CPU buffer 大小是 userspace 設置的,后面會看到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");
// 一個 struct event 變量的大小超過了 512 字節,無法放到 BPF 棧上,// 因此聲明一個 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);
  // 發送事件,參數列表   bpf_perf_event_output(ctx, &pb, BPF_F_CURRENT_CPU, e, sizeof(*e));  return 0;}

用戶空間程序

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

看一個關鍵點:使用 libbpf user-space perfbuffer_new() API 來創建一個 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);

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

3.2 ringbuf 示例

完整代碼:

  • BPF-side code

  • user-space code

內核 BPF 程序

bpf_ringbuf_output()在設計上遵循了bpf_perf_event_output()的語義, 以使應用從 perfbuf 遷移到 ringbuf 時更容易。為了看出二者有多相似,這里展示下 兩個示例代碼的 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; }

只有兩個小改動:

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

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

  • max_entries的單位是字節,必須是內核頁大小( 幾乎永遠是 4096)的倍數,也必須是 2 的冪次

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

用戶空間程序

事件 handler 簽名有點變化:

  1. 會返回錯誤信息(進而終止 consumer 循環)

  2. 參數里面去掉了產生這個事件的 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 對你很重要,那你需要自己在 BPF 代碼中記錄它。

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

第二個不同是 ringbuffer_new() API 更加簡潔:

  /* 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 遷移時無需對 BPF 代 碼做重大改動,但這也意味著它繼承了 perfbuf API 的一些缺點:

  1. 額外的內存復制(extra memory copy)

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

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

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

原理

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

  • 提前預留空間,或者能立即發現沒有可以空間了(返回NULL);

  • 預留成功后,一旦數據寫好了,將它發送到 userspace 是一個不會失敗的操作。

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

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

限制

唯一的限制是:BPF 校驗器在校驗時(at verification time), 必須知道預留數據的大小 (size of the reservation),因此不支持動態大小的事件數據。

  • 對于動態大小的數據,用戶只能退回到用bpf_ringbuf_output()方式來提交,忍受額外的數據復制開銷;

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

內核 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 完全一樣,因為這個 API 涉及到的只是提交方(生產方), 消費方還是一樣的方式來消費。

4 ringbuf 事件通知控制

4.1 事件通知開銷

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

這一點對 perfbuf 和 ringbuf 都是一樣的。

4.2 perbuf 解決方式

perfbuf 處理這種場景的方式是提供了一個采樣通知(sampled notification)機制:每 N 個事件才會發送一次通知。用戶空間創建 perfbuf 時可以指定這個參數。

這種機制能否解決問題,因具體場景而異。

4.3 ringbuf 解決方式

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

  • BPF_RB_NO_WAKEUP:不觸發通知

  • BPF_RB_FORCE_WAKEUP:會觸發通知

基于這個 flags,用戶能實現更加精確的通知控制。例子見 BPF ringbuf benchmark

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

5 總結

本文介紹了 BPF ring buffer 解決的問題及其背后的設計。

文中給出的示例代碼和內核代碼鏈接,展示了 ringbuf API 的基礎和高級用法。希望閱讀本文之后,讀者能對 ringbuf 有一個很好的理解和把握,能根據自己的具體應用 選擇合適的 API 來使用。


聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • API
    API
    +關注

    關注

    2

    文章

    1505

    瀏覽量

    62183
  • 程序
    +關注

    關注

    117

    文章

    3792

    瀏覽量

    81171
  • BPF
    BPF
    +關注

    關注

    0

    文章

    25

    瀏覽量

    4016

原文標題:[譯] BPF ring buffer:使用場景、核心設計及程序示例

文章出處:【微信號:LinuxDev,微信公眾號:Linux閱碼場】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏

    評論

    相關推薦

    MSPM0 UART通信中DMA和Ring Buffer環形緩沖的應用

    電子發燒友網站提供《MSPM0 UART通信中DMA和Ring Buffer環形緩沖的應用.pdf》資料免費下載
    發表于 09-05 11:01 ?0次下載
    MSPM0 UART通信中DMA和<b class='flag-5'>Ring</b> <b class='flag-5'>Buffer</b>環形緩沖的應用

    請問INA240中mosfet開關動作一次,振鈴ring的時間大約是多久?

    請問INA240中mosfet開關動作一次,振鈴ring的時間大約是多久?
    發表于 08-05 06:05

    通過UART0連接到esp8266與服務器進行通信,報UART Rx溢出和消息RX OVF錯誤怎么解決?

    uart.c 的控制臺上 我們增加了 uart.h 中的以下定義,因為我們的服務器可以發送的最大大小數據包為 768 字節。 #define UART_TX_BUFFER_SIZE 768//Ring
    發表于 07-11 08:05

    串口高頻接收數據的疑問求解

    RING_BUFFER_SIZE是2048, 但是一直顯示以下warning,會丟失數據Code: Select all W (8464) uart1: Ring Buffer FullW (8514) uart1
    發表于 06-12 07:28

    智能戒指Oura Ring新增測量心血管年齡與心肺功能功能

    5月10日訊,全球熱銷智能戒指品牌Oura Ring近日宣布新增功能,用戶可通過其測量心血管年齡和心肺功能,以評估心血管健康狀況并采取相應措施。
    的頭像 發表于 05-11 11:33 ?513次閱讀

    LabVIEW里Ring自定義控件,其常量無法保持同步修改,如何解決

    感覺這是LabVIEW的一個大bug。在做一些自定義映射控件時,有時用Ring控件比較好,可以直接對應非遞進的數字(相比于Enum控件),但是Ring控件很大的一個bug就是在修改自定義控件的內容
    發表于 04-24 11:20

    OpenHarmony語言基礎類庫【@ohos.buffer (Buffer)】

    Buffer對象用于表示固定長度的字節序列,是專門存放二進制數據的緩存區。
    的頭像 發表于 04-23 17:34 ?901次閱讀
    OpenHarmony語言基礎類庫【@ohos.<b class='flag-5'>buffer</b> (<b class='flag-5'>Buffer</b>)】

    三星首款智能戒指Galaxy Ring亮相

    在科技日新月異的今天,三星電子再次憑借其前沿的技術實力和創新精神,為世界帶來了令人矚目的新品——首款智能戒指Galaxy Ring。這款革命性的產品,在近日于西班牙巴塞羅那舉行的2024年世界移動通信大會(MWC 2024)上首次亮相,吸引了眾多業內人士和消費者的目光。
    的頭像 發表于 03-07 18:12 ?1312次閱讀

    三星首次公開展示智能戒指Galaxy Ring

    三星電子近日在巴塞羅那舉行的世界移動通信大會上,震撼發布其首款可穿戴智能戒指——Galaxy Ring。這款備受期待的產品標志著三星在可穿戴設備領域的又一次創新突破。
    的頭像 發表于 02-27 17:39 ?934次閱讀

    三星Galaxy Ring可穿戴設備預計今年推出

     The Verge的Allison Johnson在巴塞羅那世界移動通信大會(MWC)前,親自體驗了Galaxy Ring的原型。她稱該戒指非常纖薄并提供多種顏色選項,可能因量產影響款式。
    的頭像 發表于 02-27 10:14 ?547次閱讀

    三星Galaxy Ring可穿戴戒指首曝光,預計年內上市?

    關于Galaxy Ring的更多細節,《The Verge》高級編輯Allison Johnson曾在MWC展會上率先體驗過原型產品,并給出了一些簡要評價。她表示,雖然戒指十分小巧,但質地卻輕盈舒適,與此前想象中的大相徑庭。
    的頭像 發表于 02-26 16:04 ?776次閱讀

    蘋果智能戒指Apple Ring或即將發布,功能強大引期待

    近日,蘋果公司的一款全新智能戒指Apple Ring引發了廣泛關注。據悉,這款可穿戴設備旨在跟蹤用戶的健康數據,功能多樣,包括檢測心電圖和支持無線充電等。
    的頭像 發表于 02-25 11:35 ?1385次閱讀

    蘋果加速開發智能戒指Apple Ring,意圖與三星抗衡

    蘋果公司正在加快開發一款名為Apple Ring的智能戒指,旨在與三星即將推出的同類產品相抗衡。這款可穿戴設備設計獨特,可直接佩戴在手指上,主要功能是跟蹤用戶的健康數據。
    的頭像 發表于 02-22 14:30 ?788次閱讀

    請問MINI54 SPI BUFFER怎么用?

    各位前輩,我想請問一下,MINI 54 SPI BUFFER 怎么使用?比如怎么定義,怎么引用。哪位可以告訴我一下,不勝感激。
    發表于 01-17 08:08

    時鐘Buffer芯片是什么?其作用是啥?它被用在什么地方?

    時鐘Buffer芯片是什么?其作用是啥?它被用在什么地方? 時鐘Buffer芯片是一種用于管理和增強電子設備中的時鐘信號的集成電路。時鐘信號在數字電子設備中非常重要,它用于同步各個模塊的工作,確保
    的頭像 發表于 01-16 15:10 ?4624次閱讀
    主站蜘蛛池模板: 一级毛片免费在线播放| 欧美黄色第一页| 重口味av| 少妇仑乱A毛片| 久久久免费观成人影院| 国产av在线播放| 91久久线看在观草草青青| 性欧美video| 奇米狠狠一区二区三区| 久久精品视频在线看99| 国产精品久久久久久52AVAV| 99久久精品国产高清一区二区| 亚洲欧洲精品成人久久曰影片| 日日天干夜夜狠狠爱| 美女内射少妇一区二区四区| 国内精品偷拍在线观看| 成人无码精品1区2区3区免费看| 中文字幕在线观看亚洲| 亚洲高清国产拍精品动图| 少妇高潮惨叫久久久久久欧美 | 永久免费无码AV国产网站| 天海翼精品久久中文字幕| 热久久视久久精品18| 欧美高清视频www夜色资源网| 久久高清免费视频| 国产制服丝袜91在线| 国产69精品麻豆久久久久| 成 人 免费 黄 色 网站无毒下载| 97人妻丰满熟妇AV无码| 最近2019中文字幕免费| 又黄又爽又无遮挡在线观看免费 | 大桥未久电影在线| japanese色系free日本| 97视频在线观看免费视频| 2021精品乱码多人收藏| 中文字幕一区久久久久| 中文字幕一区在线观看视频| 在线亚洲中文字幕36页| 在线亚洲免费| 91综合久久久久婷婷| 91麻豆精品国产一级|