eBPF初体验

eBPF(aka extended berkeley package filter)是最近比较热门的内核调试方法和工具。

我个人目前还是更喜欢用trace-cmd+ftrace来调试内核的问题,但ebpf据说是可以更加灵活的设置调试信息。ebpf的原理是使用tracepoint静态监测点和kprobe动态监测点来对内核进行函数级别的监测,可以监测内核运行的代码流是否正确,更可以监控内核运行的效率。

我在github上fork了一份bcc(https://github.com/iovisor/bcc)的代码,然后按照(https://github.com/chensong2000/bcc/blob/master/INSTALL.md)的步骤开始环境搭建。

如果是ubuntu官方的内核,可以直接安装,

sudo apt-get install bpfcc-tools linux-headers-$(uname -r)

但我的内核是自己编译的,并且打了preempt rt的patch,所以要使用源代码进行编译,步骤如下:

1,内核配置:

CONFIG_BPF=y
CONFIG_BPF_SYSCALL=y
# [optional, for tc filters]
CONFIG_NET_CLS_BPF=m
# [optional, for tc actions]
CONFIG_NET_ACT_BPF=m
CONFIG_BPF_JIT=y
# [for Linux kernel versions 4.1 through 4.6]
CONFIG_HAVE_BPF_JIT=y
# [for Linux kernel versions 4.7 and later]
CONFIG_HAVE_EBPF_JIT=y
# [optional, for kprobes]
CONFIG_BPF_EVENTS=y

我在我的台式机上运行的时候,由于内核没打开CONFIG_BPF_SYSCALL,ebpf是不能正常工作的。

2,git clone https://github.com/chensong2000/bcc.git

3,编译依赖的工具和库

VER=trusty
echo "deb http://llvm.org/apt/$VER/ llvm-toolchain-$VER-3.7 main
deb-src http://llvm.org/apt/$VER/ llvm-toolchain-$VER-3.7 main" | \
  sudo tee /etc/apt/sources.list.d/llvm.list

完成这个步骤后/etc/apt/sources.list.d/llvm.list的内容为:deb http://llvm.org/apt/trusty/ llvm-toolchain-trusty-3.7 main deb-src http://llvm.org/apt/trusty/llvm-toolchain-trusty-3.7 main

接下来:

wget -O - http://llvm.org/apt/llvm-snapshot.gpg.key | sudo apt-key add -
sudo apt-get update

sudo apt-get -y install bison build-essential cmake flex git libedit-dev \
  libllvm6.0 llvm-6.0-dev libclang-6.0-dev python zlib1g-dev libelf-dev

4,编译bcc

1)mkdir bcc/build; cd bcc/build
2)cmake ..
3)make
4)sudo make install
5)cmake -DPYTHON_CMD=python3 .. # build python3 binding
6)pushd src/python/
7)make
8)sudo make install
9)popd

5,测试一下

1)sudo ./examples/hello_world.py

终于:

sendmail-mta-1558  [000] d...1.. 17522.673856: 0x00000001: Hello, World!

            sshd-676   [000] d...1.. 17532.211253: 0x00000001: Hello, World!
            sshd-13326 [000] d...1.. 17532.291607: 0x00000001: Hello, World!
            sshd-13326 [000] d...1.. 17535.026392: 0x00000001: Hello, World!
              sh-13328 [000] d...1.. 17535.061753: 0x00000001: Hello, World!
       run-parts-13329 [000] d...1.. 17535.082350: 0x00000001: Hello, World!
       00-header-13330 [000] d...1.. 17535.092330: 0x00000001: Hello, World!
       00-header-13330 [000] d...1.. 17535.106963: 0x00000001: Hello, World!
       00-header-13330 [000] d...1.. 17535.126849: 0x00000001: Hello, World!
       run-parts-13329 [000] d...1.. 17535.144171: 0x00000001: Hello, World!


2)sudo  ./examples/tracing/hello_fields.py

TIME(s)            COMM             PID    MESSAGE
17556.356978000    bash             13339  Hello, World!
17562.243536000    Socket Thread    13127  Hello, World!
17563.492875000    bash             13339  Hello, World!

3) sudo ./examples/tracing/disksnoop.py

TIME(s)            T  BYTES    LAT(ms)
17586.641013000    M  0           0.09
17588.720995000    M  0           0.09
17589.522525000    W  0           0.54
17589.526587000    W  0           3.97
17589.530379000    M  0           3.25
17589.531839000    W  0           1.38
17589.532644000    M  0           0.74
17590.801002000    M  0           0.09
17592.881071000    M  0           0.12
17594.563566000    W  0           1.62
17594.566741000    W  0           3.12
17594.571347000    W  0           3.77
17594.574464000    M  0           3.03
17594.575920000    W  0           1.38

上述几个例子eBPF都是通过kprobe hook到如sys_clone,sys_sync,blk_mq_start_request这样的内核函数中,也就是说,当这些函数被调用的时候,eBPF会在终端上显示你想要的内容,所以,你可以在另一个终端运行一些进程,或者做一些io操作,eBPF所在终端就能显示出来。

6,代码分析

1)hello_world.py

from bcc import BPF

# This may not work for 4.17 on x64, you need replace kprobe__sys_clone with kprobe____x64_sys_clone
BPF(text='int kprobe__sys_clone(void *ctx) { bpf_trace_printk("Hello, World!\\n"); return 0; }').trace_print()

BPF(text=‘’内部写的就是C代码,表示使用kprobe动态调试sys_clone,当内核调用sys_clone函数时,执行bpf_trace_printk("Hello, World!\\n")

2)biosnoop.py

b = BPF(text="""
#include <uapi/linux/ptrace.h>
#include <linux/blkdev.h>

BPF_HASH(start, struct request *);

void trace_start(struct pt_regs *ctx, struct request *req) {
    // stash start timestamp by request ptr
    u64 ts = bpf_ktime_get_ns();

    start.update(&req, &ts);
}

void trace_completion(struct pt_regs *ctx, struct request *req) {
    u64 *tsp, delta;

    tsp = start.lookup(&req);
    if (tsp != 0) {
        delta = bpf_ktime_get_ns() - *tsp;
        bpf_trace_printk("%d %x %d\\n", req->__data_len,
            req->cmd_flags, delta / 1000);
        start.delete(&req);
    }
}
""")

if BPF.get_kprobe_functions(b'blk_start_request'):
        b.attach_kprobe(event="blk_start_request", fn_name="trace_start")
b.attach_kprobe(event="blk_mq_start_request", fn_name="trace_start")
b.attach_kprobe(event="blk_account_io_done", fn_name="trace_completion")

同理b = BPF(text=""中是C代码,定义了两个函数trace_start和trace_completion,b.attach_kprobe(event="blk_start_request", fn_name="trace_start"),表示内核调用blk_start_request的时候会运行trace_start,同理blk_mq_start_request也是。blk_account_io_done会运行trace_completion。

在trace_start和trace_completion中使用start来保存函数调用的时间,并用来计算两个函数调用的流失的时间,可以看出来一个bio处理的延时。

 

先写这些,进一步使用会有进一步的体验,也会有进一步的分享。

参考:

https://github.com/iovisor/bcc/blob/master/docs/tutorial_bcc_python_developer.md

https://github.com/iovisor/bcc/blob/master/docs/reference_guide.md

https://github.com/chensong2000/bcc/blob/master/INSTALL.md

 

 

03-21
### eBPF 技术概述及其在网络编程中的应用案例 #### 什么是 eBPFeBPF(extended Berkeley Packet Filter)是一种运行在 Linux 内核中的虚拟机技术,允许开发者编写安全高效的程序片段并将其加载到内核中执行。最初设计用于数据包过滤,但现在已扩展为支持多种用例,包括性能监控、跟踪和安全性分析[^1]。 #### eBPF 的核心特性 eBPF 提供了一系列强大的功能,使其成为现代系统管理和调试工具的重要组成部分: - **高效性**:通过 JIT 编译器优化字节码,使 eBPF 程序能够接近原生代码的速度运行。 - **灵活性**:支持各种钩子点(hook points),例如网络事件、文件操作、进程活动等,从而实现广泛的场景覆盖。 - **安全性**:内置验证机制确保加载的 eBPF 程序不会破坏内核稳定性或引发安全隐患。 #### eBPF 在编程与网络领域的主要应用场景 以下是几个典型的应用实例: ##### 性能监测 利用 eBPF 可以实时捕获系统的低级细节而无需修改应用程序源码或者重启服务。例如,`bcc` 工具集提供了多个脚本来帮助管理员诊断延迟瓶颈以及资源争用情况。 ##### 安全审计 借助 eBPF 实现细粒度的安全策略管理变得可行。像 Falco 这样的项目就是基于此构建起来的一个容器威胁检测平台;它会监视可疑行为并向运维人员发出警报信号。 ##### 数据平面加速 对于 SDN 和 NFV 场景而言,传统软件定义方式往往存在较高的处理开销。然而凭借 eBPF/XDP 组合方案,则可以在靠近硬件层面快速转发流量的同时完成复杂的业务逻辑判断工作——既保留了可编程能力又兼顾到了线速需求。 ```python from bcc import BPF program = """ int hello(void *ctx) { bpf_trace_printk("Hello, World!\\n"); return 0; } """ b = BPF(text=program) fn = b.load_func("hello", BPF.KPROBE) b.attach_kprobe(event="__x64_sys_clone", fn_name="hello") print(b.trace_fields()) ``` 上述 Python 脚本展示了如何使用 `bcc` 库创建简单的 kprobes 来追踪每次克隆调用的发生时刻,并打印相应的消息至控制台日志流当中去。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值