Linux內核觀測技術BP
https://www.lijiaocn.com/%E6%8A%80%E5%B7%A7/2019/02/25/ebpf-introduction-1.html
eBPF是kernel 3.15中引入的全新設計,將原先的BPF發展成一個指令集更復雜、應用範圍更廣的「內核虛擬機器」。
eBPF支援在使用者態將C語言編寫的一小段「內核程式碼」注入到內核中執行,注入時要先用llvm編譯得到使用BPF指令集的elf檔案,然後從elf檔案中解析出可以注入內核的部分,最後用bpf_load_program方法完成注入。 使用者態程式和注入到內核中的程式通過共用一個位於內核中map
實現通訊。爲了防止注入的程式碼導致內核崩潰,eBPF會對注入的程式碼進行嚴格檢查,拒絕不合格的程式碼的注入。
BCC是一個python庫,實現了map建立、程式碼編譯、解析、注入等操作,使開發人員只需聚焦於用C語言開發要注入的內核程式碼。 文章eBPF簡史從eBPF的前身BPF講起,將eBPF的來龍去脈介紹的很明白,是難得的好文章,建議直接過去閱讀,這裏就不摘抄了。 BPF: the universal in-kernel virtual machine介紹了BPF從網路子系統中的報文複製功能到內核通用虛擬機器eBPF的演變過程。
BPF是很早就有的內核特性,在內核中將報文「映象」了一份,並用BPF指令檢查映象出來的報文、決定報文的去留,即在1)拋棄報文和2)將其複製到使用者空間之間抉擇。
具體實現就不去瞭解了,BPF好歹設計了一套指令集,雖然比較簡單,但沒有紮實的編譯原理基礎,估計一時半會兒也看不懂,這裏只收集一下相關文件:
另外學到了一個新知識,tcpdump使用的libpcap是基於BPF的,在使用tcpdump或者libpcap時傳入的「host 192.168.1.1」、「tcp and port 80」等是過濾表達式
。
過濾表達式會被編譯成BPF指令
,在tcpdump命令後面加上-d
參數可以看到:
$ tcpdump -d -i eth0 tcp and port 80
(000) ldh [12]
(001) jeq #0x86dd jt 2 jf 8
(002) ldb [20]
(003) jeq #0x6 jt 4 jf 19
(004) ldh [54]
(005) jeq #0x50 jt 18 jf 6
(006) ldh [56]
(007) jeq #0x50 jt 18 jf 19
(008) jeq #0x800 jt 9 jf 19
(009) ldb [23]
(010) jeq #0x6 jt 11 jf 19
(011) ldh [20]
(012) jset #0x1fff jt 19 jf 13
(013) ldxb 4*([14]&0xf)
(014) ldh [x + 14]
(015) jeq #0x50 jt 18 jf 16
(016) ldh [x + 16]
(017) jeq #0x50 jt 18 jf 19
(018) ret #65535
(019) ret #0
這些BPF指令是在內核中被BPF解釋執行的。
原先的BPF依然支援,用cBPF指代。eBPF全新設計了更豐富的指令集、增加了暫存器,效能大幅提高:
The original patch that added support for eBPF in the 3.15 kernel showed that eBPF was up to four times faster on x86-64 than the old classic BPF (cBPF) implementation for some network filter microbenchmarks, and most were 1.5 times faster.
增加了名爲bpf
的系統呼叫,爲使用者態程式提供與內核中的eBPF進行互動的途徑:
int bpf(int cmd, union bpf_attr *attr, unsigned int size);
cmd
是eBPF支援的cmd,分爲三類: 操作注入的程式碼、操作用於通訊的map、前兩個操作的混合。
通過eBPF可以做更多的事情,不再僅僅是進行報文複製和過濾,網路方面可以切入到更深的層次,在更「靠前」的階段進行幹預,例如XDP藉助eBPF在報文剛收到的時候就進行幹預,使用XDP(eXpress Data Path)防禦DDoS攻擊。
還可以:
限制進程可以使用的系統呼叫,seccomp;
輸出內核中的數據,進行內核偵錯、效能分析、呼叫跟蹤;
偵錯使用者態程式,Userland Statically Defined Tracepoints ;
eBPF是kernel 3.15開始支援的,在kernel 3.17原始碼中獲得了一個單獨的bpf目錄,建議直接使用3.17以及以上版本的內核。下面 下麪這四篇文章可以幫你搭建對eBPF的認知框架:
見:CentOS7/6內核升級的簡單方法:藉助ELRepo,用yum命令更新內核。
samples/bpf,暫時沒有找到相關資料,目前以學習bcc的使用爲主,2019-02-25 18:11:40
同上一節。
BCC首先提供了一個名爲bcc的python庫,簡化了eBPF應用的開發過程,然後它收集大量的eBPF應用,主要是效能分析相關的。 可以直接使用bcc已經收集的命令調查問題(bcc Tutorial),也可以用bcc提供的python庫自己開發eBPF應用(bcc Python Developer Tutorial)。
Kernel版本需要是4.1以上(2019-02-25 18:08:15),並且要安裝kernel-devel,如果沒有安裝kernel-devel,eBPF應用會彙編譯失敗(bcc收集的命令執行時也會出現下面 下麪錯誤):
# execsnoop
chdir(/lib/modules/4.20.12-1.el7.elrepo.x86_64/build): No such file or directory
Traceback (most recent call last):
File "/usr/share/bcc/tools/execsnoop", line 166, in <module>
b = BPF(text=bpf_text)
File "/usr/lib/python2.7/site-packages/bcc/__init__.py", line 318, in __init__
raise Exception("Failed to compile BPF text")
Exception: Failed to compile BPF text
直接用yum安裝:
yum install bcc-tools
用rpm -ql
可以看到所有bcc檔案,tools/
目錄中是bcc提供的命令,可以用man
檢視對應的手冊,比如man bcc-argdist
:
$ rpm -ql bcc-tools
/usr/share/bcc
/usr/share/bcc/introspection
/usr/share/bcc/introspection/bps
/usr/share/bcc/tools
/usr/share/bcc/tools/argdist
/usr/share/bcc/tools/bashreadline
/usr/share/bcc/tools/biolatency
...
/usr/share/bcc/tools/doc
/usr/share/bcc/tools/doc/argdist_example.txt
/usr/share/bcc/tools/doc/bashreadline_example.txt
/usr/share/bcc/tools/doc/biolatency_example.txt
/usr/share/bcc/tools/doc/biosnoop_example.txt
...
/usr/share/man/man8/bcc-argdist.8.gz
/usr/share/man/man8/bcc-bashreadline.8.gz
/usr/share/man/man8/bcc-biolatency.8.gz
/usr/share/man/man8/bcc-biosnoop.8.gz
/usr/share/man/man8/bcc-biotop.8.gz
BCC的命令較多,單開一篇筆記,這篇到此爲止。