成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

探索 Ebpf 在 Node.Js 中的應用

開發 前端
ebpf 是現代 Linux 內核提供的非常復雜和強大的技術,它使得 Linux 內核變得可編程,不再是完全的黑盒子。隨著 ebpf 的發展和成熟,其應用也越來越廣泛,本文介紹如何使用 ebpf 來追蹤 Node.js 底層的代碼。

 [[437683]]

前言

ebpf 是現代 Linux 內核提供的非常復雜和強大的技術,它使得 Linux 內核變得可編程,不再是完全的黑盒子。隨著 ebpf 的發展和成熟,其應用也越來越廣泛,本文介紹如何使用 ebpf 來追蹤 Node.js 底層的代碼。

介紹

ebpf 的設計思想雖然很簡單,但是實現和使用上非常復雜。ebpf 本質上內核實現了一個虛擬機,用戶可以把自己編寫的 c 代碼加載進內核中執行,從而參與內核的邏輯處理。這聽起來很簡單,但是整個技術其實非常復雜,從實現來說,內核需要對加載的代碼進行非常多而復雜的校驗,以保證安全性,內核還需要實現一個虛擬機來執行用戶的代碼和在內核代碼中加入支持 ebpf 機制的邏輯。從使用來說,使用或編寫 ebpf 代碼對我們來說成本非常高,我們需要學會搭建環境,需要了解如何編譯 ebpf 程序,甚至還需要了解 Linux 內核的一些知識。不過隨著 ebpf 多年的發展,這種情況已經改善了很多。ebpf 的介紹在網上有很多,這里就不多介紹。

使用

下面來看一下如何基于 libbpf 寫一個 ebpf 程序。ebpf 程序分為兩個部分,第一部分是 ebpf 代碼。hello.bpf.c

  1. #include <linux/bpf.h> 
  2. #include <bpf/bpf_helpers.h> 
  3.  
  4. SEC("tracepoint/syscalls/sys_enter_execve"
  5. int handle_tp(void *ctx){ 
  6.     int pid = bpf_get_current_pid_tgid()>> 32; 
  7.     char fmt[] = "BPF triggered from PID %d.\n"
  8.     bpf_trace_printk(fmt, sizeof(fmt), pid); 
  9.     return 0; 
  10.  
  11. char LICENSE[] SEC("license") = "Dual BSD/GPL"

以上是被加載進內核執行的代碼,主要是利用內核的 tracepoint 機制,給 sys_enter_execve 函數插入一個鉤子,每次執行到這個函數時,鉤子函數就會被執行。另一部分是負責把 ebpf 代碼加載進內核的代碼。hello.c

  1. #include <stdio.h> 
  2. #include <stdlib.h> 
  3. #include <string.h> 
  4. #include <assert.h> 
  5. #include <errno.h> 
  6. #include <fcntl.h> 
  7. #include <unistd.h> 
  8. #include <sys/resource.h> 
  9. #include <bpf/libbpf.h> 
  10. #include "hello.skel.h" 
  11.  
  12. int main(int argc, char **argv){ 
  13.     struct hello_bpf *skel; 
  14.     int err; 
  15.  
  16.     /* Open BPF application */ 
  17.     skel = hello_bpf__open(); 
  18.     /* Load & verify BPF programs */ 
  19.     err = hello_bpf__load(skel); 
  20.     /* Attach tracepoint handler */ 
  21.     err = hello_bpf__attach(skel); 
  22.     printf("Hello BPF started, hit Ctrl+C to stop!\n"); 
  23.     // output 
  24.     read_trace_pipe(); 
  25.  
  26. cleanup: 
  27.     hello_bpf__destroy(skel); 
  28.     return -err; 

這里只列出核心的代碼,hello.c 的邏輯很簡單,打開 ebpf 然后加載到內核,最后查看 ebpf 程序的輸入。這就是 ebpf 程序的整體邏輯,過程都差不多,重點是確定我們需要做什么事情,然后寫不同的代碼。最后,如果不再需要追蹤的時候,可以銷毀 ebpf 代碼。

應用

在 ebpf 之前,內核對我們來說是一個黑盒子。有了 ebpf 之后,內核對我們透明了很多。但是軟件是分層的,我們平時直接和內核打交道并不多,我們更關心上層軟件的情況。具體來說,當我們使用一個 Node.js 的時候,除了關心業務代碼,我們也需要關心 Node.js 本身的代碼。但是 Node.js 對我們來說也是個黑盒子,我們不知道它具體做了什么事情或者某一個時刻的運行狀態,這樣非常不利于我們排查問題或者了解系統的運行情況。有了 ebpf 后,我們就可以做更多的事情了。Linux 內核提供了非常多的代碼追蹤技術,其中有一種是 uprobe,uprobe 是一種動態追蹤應用代碼的技術,比如我們想了解 Node.js 的 Libuv 中的 uv_tcp_listen 函數,那么我們就可以通過 ebpf 去實現這種效果。有了這種能力,我們就可以掌握系統更多的數據和信息。

實現

應用層使用 uprobe 比 kprobe 復雜,kprobe 是用于追蹤內核函數,因為內核知道它的函數對應的虛擬地址,所以我們只需要告訴它函數名就可以實現對該函數的追蹤,但是 uprobe 則不一樣,uprobe 是用于追蹤應用層代碼的,內核并不知道或者說不應該關注某個函數對應的虛擬地址,所以這個難題需要應用層解決。下面來看一下具體的實現。uprobe.bpf.c

  1. #include <linux/bpf.h> 
  2. #include <linux/ptrace.h> 
  3. #include <bpf/bpf_helpers.h> 
  4. #include <bpf/bpf_tracing.h> 
  5. #include "uv.h" 
  6.  
  7. char LICENSE[] SEC("license") = "Dual BSD/GPL"
  8. SEC("uprobe/uv_tcp_listen"
  9. int BPF_KPROBE(uprobe, uv_tcp_t* tcp, int backlog, uv_connection_cb cb){ 
  10.     bpf_printk("uv_tcp_listen start %d \n", backlog); 
  11.     return 0; 
  12.  
  13. SEC("uretprobe/uv_tcp_listen"
  14. int BPF_KRETPROBE(uretprobe, int ret){ 
  15.     bpf_printk("uv_tcp_listen end %d \n", ret); 
  16.     return 0; 

這里我們實現了對 libuv 的 uv_tcp_listen 函數進行追蹤,包括函數開始執行和執行完畢兩個追蹤點。定義完 ebpf 程序后,來看一下如何加載到內核。uprobe.c

  1. int main(int argc, char **argv){ 
  2.     struct uprobe_bpf *skel; 
  3.     long base_addr, uprobe_offset; 
  4.     int err, i; 
  5.         // 要追蹤的可執行文件 
  6.     char execpath[50] = "/usr/bin/node"
  7.     char * func = "uv_tcp_listen"
  8.         // 計算某個函數在可執行文件里的地址偏移 
  9.     uprobe_offset = get_elf_func_offset(execpath, func); 
  10.  
  11.     /* Load and verify BPF application */ 
  12.     skel = uprobe_bpf__open_and_load(); 
  13.  
  14.     /* Attach tracepoint handler */ 
  15.     skel->links.uprobe = bpf_program__attach_uprobe(skel->progs.uprobe, 
  16.                             false /* not uretprobe */, 
  17.                             -1, /* any pid */ 
  18.                             execpath, 
  19.                             uprobe_offset); 
  20.  
  21.     skel->links.uretprobe = bpf_program__attach_uprobe(skel->progs.uretprobe, 
  22.                                true /* uretprobe */, 
  23.                                -1 /* any pid */, 
  24.                                execpath, 
  25.                                uprobe_offset); 
  26.  
  27.         // ... 
  28. cleanup: 
  29.     uprobe_bpf__destroy(skel); 
  30.     return -err; 

uprobe.c 的重點在于計算某個函數在某個可執行文件的地址信息,這個主要是利用 elf 文件來判斷,elf 是代碼編譯后生成的一個可執行文件,它里面可以記錄了關于可執行文件的一些元數據(也可以通過 readelf -Ws exen_file 查看),比如符號表里記錄了函數的信息,拿到相關信息后,設置 uprobe 和 uretprobe就可以了。通過上面的 ebpf 代碼,我們就可以追蹤到 uv_tcp_listen 函數的調用情況,有了這種能力,我們就可以隨便監聽自己想監聽的函數。除了 uprobe 之后,我們還可以利用內核的 kprobe 監聽內核函數。比如下面的 ebpf 代碼就可以實現對創建進程的追蹤。

  1. SEC("kprobe/__x64_sys_execve"
  2. int BPF_KPROBE(__x64_sys_execve){ 
  3.     pid_t pid; 
  4.     pid = bpf_get_current_pid_tgid() >> 32; 
  5.     bpf_printk("KPROBE ENTRY pid = %d", pid); 
  6.     return 0; 
  7.  
  8. SEC("kretprobe/__x64_sys_execve"
  9. int BPF_KRETPROBE(__x64_sys_execve_exit){ 
  10.     pid_t pid; 
  11.  
  12.     pid = bpf_get_current_pid_tgid() >> 32; 
  13.     bpf_printk("KPROBE EXIT: pid = %d\n", pid); 
  14.     return 0; 

總結

簡單地介紹了一下強大的 ebpf 技術和在 Node.js 中的應用,但是這只是個簡單的例子,我們還有很多事情需要做,比如能否結合 addon 來使用,如何支持動態能力等等。另外因為 C++ 代碼編譯后的函數名和原來的是不太一樣的,這可能會導致我們通過函數名找虛擬地址時找不到,這里也還有很多需要研究的地方。總的來說,ebpf 不僅對 Node.js 來說非常有價值,對其他應用層來說意義也是一樣的。這是一個非常值得探索的技術方向。

代碼倉庫:https://github.com/theanarkh/libbpf-code

 

責任編輯:武曉燕 來源: 編程雜技
相關推薦

2020-12-08 06:28:47

Node.js異步迭代器

2021-04-06 10:15:29

Node.jsHooks前端

2021-12-18 07:42:15

Ebpf 監控 Node.js

2023-06-30 08:05:41

2025-01-13 00:00:00

2020-07-31 13:35:34

Node.js應用分析前端

2017-04-10 13:28:32

Node.jsJavaScript

2014-03-07 13:43:32

Node.jsNode

2021-08-20 09:00:00

Node.js開發API

2024-01-05 08:49:15

Node.js異步編程

2021-05-21 09:36:42

開發技能代碼

2016-08-11 14:02:02

NodeJS前端

2020-04-15 15:48:03

Node.jsstream前端

2011-11-10 11:08:34

Node.js

2013-11-01 09:34:56

Node.js技術

2015-03-10 10:59:18

Node.js開發指南基礎介紹

2022-01-11 17:23:12

配置Node.jsNode

2012-03-09 09:11:29

Node.js

2017-03-19 16:40:28

漏洞Node.js內存泄漏

2017-03-20 13:43:51

Node.js內存泄漏
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 免费一区二区三区 | 亚洲精品乱码久久久久久蜜桃91 | 性大毛片视频 | 免费九九视频 | 本地毛片 | 黄色一级电影在线观看 | 日韩精品一区二区三区在线观看 | 古装人性做爰av网站 | 亚洲高清视频一区二区 | 亚洲精品9999 | 国产精品美女久久久久aⅴ国产馆 | 人人爽人人草 | 伊人网国产 | 国产精品福利一区二区三区 | 欧美成人免费在线视频 | 91最新视频 | 国产一区不卡 | 嫩呦国产一区二区三区av | 我爱操 | 一区二区三区欧美 | 久久91精品 | tube国产 | 日韩精品一区二区三区四区视频 | 亚洲一视频 | 狠狠入ady亚洲精品经典电影 | 日日综合 | 国产精品99久久久久久动医院 | 久久久人成影片一区二区三区 | 国产视频第一页 | 一区二区免费在线观看 | 亚洲精品乱码久久久久久9色 | 久久久久久久久精 | 二区国产| 精品视频999 | 欧美激情一区 | 午夜影晥 | 日韩精品一区二区三区高清免费 | 国产免费av在线 | 五月天婷婷久久 | 日本激情视频在线播放 | 国产日韩欧美在线 |