当前位置:网站首页>龙蜥社区开源 coolbpf,BPF 程序开发效率提升百倍
龙蜥社区开源 coolbpf,BPF 程序开发效率提升百倍
2022-07-01 13:18:00 【InfoQ】
引言
- 系统故障诊断:它可以动态插桩透视内核。
- 网络性能优化:它可以对接收和发送的网络包做修改和转发。
- 系统安全:它可以监控文件打开和关闭从而做出安全决策等。
- 性能监控:它可以查看函数耗费时间从而知道性能瓶颈点。
一、BPF 开发方式对比
1、原始阶段
static struct sock_filter filter[6] = {
{ OP_LDH, 0, 0, 12 }, // ldh [12]
{ OP_JEQ, 0, 2, ETH_P_IP }, // jeq #0x800, L2, L5
{ OP_LDB, 0, 0, 23 }, // ldb [23]
{ OP_JEQ, 0, 1, IPPROTO_TCP }, // jeq #0x6, L4, L5
{ OP_RET, 0, 0, 0 }, // ret #0x0
{ OP_RET, 0, 0, -1, }, // ret #0xffffffff
};
int main(int argc, char **argv)
{
…
struct sock_fprog prog = { 6, filter };
…
sock = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
…
if (setsockopt(sock, SOL_SOCKET, SO_ATTACH_FILTER, &prog, sizeof(prog))) {
return 1;
}
…
}
2、保守阶段
struct {
__uint(type, BPF_MAP_TYPE_ARRAY);
__type(key, u32);
__type(value, long);
__uint(max_entries, 256);
} my_map SEC(".maps");
SEC("socket1")
int bpf_prog1(struct __sk_buff *skb)
{
int index = load_byte(skb, ETH_HLEN + offsetof(struct iphdr, protocol));
long *value;
if (skb->pkt_type != PACKET_OUTGOING)
return 0;
value = bpf_map_lookup_elem(&my_map, &index);
if (value)
__sync_fetch_and_add(value, skb->len);
return 0;
}
char _license[] SEC("license") = "GPL";
int main(int ac, char **argv)
{
struct bpf_object *obj;
struct bpf_program *prog;
int map_fd, prog_fd;
char filename[256];
int i, sock, err;
FILE *f;
snprintf(filename, sizeof(filename), "%s_kern.o", argv[0]);
obj = bpf_object__open_file(filename, NULL);
if (libbpf_get_error(obj))
return 1;
prog = bpf_object__next_program(obj, NULL);
bpf_program__set_type(prog, BPF_PROG_TYPE_SOCKET_FILTER);
err = bpf_object__load(obj);
if (err)
return 1;
prog_fd = bpf_program__fd(prog);
map_fd = bpf_object__find_map_fd_by_name(obj, "my_map");
...
}
3、BCC 初始阶段
int trace_connect_v4_entry(struct pt_regs *ctx, struct sock *sk)
{
if (container_should_be_filtered()) {
return 0;
}
u64 pid = bpf_get_current_pid_tgid();
##FILTER_PID##
u16 family = sk->__sk_common.skc_family;
##FILTER_FAMILY##
// stash the sock ptr for lookup on return
connectsock.update(&pid, &sk);
return 0;
}
# initialize BPF
b = BPF(text=bpf_text)
if args.ipv4:
b.attach_kprobe(event="tcp_v4_connect", fn_name="trace_connect_v4_entry")
b.attach_kretprobe(event="tcp_v4_connect", fn_name="trace_connect_v4_return")
b.attach_kprobe(event="tcp_close", fn_name="trace_close_entry")
b.attach_kretprobe(event="inet_csk_accept", fn_name="trace_accept_return")
4、BCC 高级阶段
SEC("kprobe/inet_listen")
int BPF_KPROBE(inet_listen_entry, struct socket *sock, int backlog)
{
__u64 pid_tgid = bpf_get_current_pid_tgid();
__u32 pid = pid_tgid >> 32;
__u32 tid = (__u32)pid_tgid;
struct event event = {};
if (target_pid && target_pid != pid)
return 0;
fill_event(&event, sock);
event.pid = pid;
event.backlog = backlog;
bpf_map_update_elem(&values, &tid, &event, BPF_ANY);
return 0;
}
#include "solisten.skel.h"
...
int main(int argc, char **argv)
{
...
libbpf_set_strict_mode(LIBBPF_STRICT_ALL);
libbpf_set_print(libbpf_print_fn);
obj = solisten_bpf__open();
obj->rodata->target_pid = target_pid;
err = solisten_bpf__load(obj);
err = solisten_bpf__attach(obj);
pb = perf_buffer__new(bpf_map__fd(obj->maps.events), PERF_BUFFER_PAGES,
handle_event, handle_lost_events, NULL, NULL);
...
}
5、资源共享阶段
- 开箱即用:内核侧仅提供 bpf.c 即可,完全剥离出内核编译工程。
- 复用编译成果:本地侧无编译过程,不存在库依赖和 CPU、内存等资源消耗问题。
- 自适应不同版本差异:更适合在集群多个不同内核版本共存的场景。
先在本地安装coolbpf,里面带的命令会把xx.bpf.c发送到编译服务器编译。
pip install coolbpf
...
import time
from pylcc.lbcBase import ClbcBase
bpfPog = r"""
#include "lbc.h"
SEC("kprobe/wake_up_new_task")
int j_wake_up_new_task(struct pt_regs *ctx)
{
struct task_struct* parent = (struct task_struct *)PT_REGS_PARM1(ctx);
bpf_printk("hello lcc, parent: %d\n", _(parent->tgid));
return 0;
}
char _license[] SEC("license") = "GPL";
"""
class Chello(ClbcBase):
def __init__(self):
super(Chello, self).__init__("hello", bpf_str=bpfPog)
while True:
time.sleep(1)
if __name__ == "__main__":
hello = Chello()
pass
二、coolbpf 功能及架构

1)本地编译服务,基础库封装:客户使用本地容器镜像编译程序,调用封装的通用函数库简化程序编写和数据处理。
2)远程编译服务:接收 bpf.c,生成 bpf.so 或 bpf.o,提供给高级语言进行加载,用户只专注自己的功能开发,不用关心底层库安装、环境搭建。
3)高版本特性通过 kernel module 方式补齐到低版本,如 ring buffer 特性,backport BPF 特性到 3.10 内核。
4)BTF 的自动生成和全网最新内核版本爬虫。自动发现最新的 CentOS、ubuntu、Anolis 等内核版本,自动生成对应的 BTF。
5)各内核版本功能测试自动化,工具编写后自动进行安装测试,保障用户功能在生产环境运行前预测试。
6)Python、Rust、Go、C 等高级语言支持。

三、实践说明
1、pylcc(基于 Python 的 LCC)

bpfPog = r"""
#include "lbc.h"
LBC_PERF_OUTPUT(e_out, struct data_t, 128);
LBC_HASH(pid_cnt, u32, u32, 1024);
LBC_STACK(call_stack,32);
importtimefrompylcc.lbcBaseimportClbcBase
classPingtrace(ClbcBase):def__init__(self):super(Pingtrace, self).__init__("pingtrace")
#include "vmlinux.h"
#include <linux/types.h>
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_core_read.h>
#include <bpf/bpf_tracing.h>
2、rlcc(基于 Rust 的 LCC)
编译example流程:
SKEL_RS=1 cargo build --release 生成 rust skel 文件;
SKEL_RS=0 cargo build --release 无需在生成 rust skel 文件;
默认 SKEL_RS 为 1.
编译rexample流程:
rexample 使用了远程编译功能,具体编译流程如下:
运行命令 mkdir build & cd build 创建编译目录;
运行命令 cmake .. 生成 Makefile 文件;
运行命令 make rexample;
运行 example 程序: ../lcc/rlcc/rexample/target/release/rexample.
fn main() -> Result<()>{
let opts = Command::from_args();
let mut skel_builder = ExampleSkelBuilder::default();
if opts.verbose {
skel_builder.obj_builder.debug(true);
}
bump_memlock_rlimit()?;
let mut open_skel = skel_builder.open()?;
let mut skel = open_skel.load()?;
skel.attach()?;
let perf = PerfBufferBuilder::new(skel.maps_mut().events())
.sample_cb(handle_event)
.lost_cb(handle_lost_events)
.build()?;
loop {
perf.poll(Duration::from_millis(100))?;
}
}
3、glcc(generic LCC,高版本特性移植到低版本)
- 目前基于 eBPF 编写的程序只能在高版本内核(支持 eBPF 的内核)上运行,无法在不支持 eBPF 功能的内核上运行。
- 线上有很多 Alios 或者 CentOS 低版本内核需要维护。
- 存量 BPF 工具或项目代码,希望不做修改能跨内核运行。


#define IOCTL_BPF_MAP_CREATE _IOW(';', 0, union bpf_attr *)
#define IOCTL_BPF_MAP_LOOKUP_ELEM _IOWR(';', 1, union bpf_attr *)
#define IOCTL_BPF_MAP_UPDATE_ELEM _IOW(';', 2, union bpf_attr *)
#define IOCTL_BPF_MAP_DELETE_ELEM _IOW(';', 3, union bpf_attr *)
#define IOCTL_BPF_MAP_GET_NEXT_KEY _IOW(';', 4, union bpf_attr *)
#define IOCTL_BPF_PROG_LOAD _IOW(';', 5, union bpf_attr *)
#define IOCTL_BPF_PROG_ATTACH _IOW(';', 6, __u32)
#define IOCTL_BPF_PROG_FUNCNAME _IOW(';', 7, char *)
#define IOCTL_BPF_OBJ_GET_INFO_BY_FD _IOWR(';', 8, union bpf_attr *)
四、总结
边栏推荐
- Function test process in software testing
- 面试题目总结(1) https中间人攻击,ConcurrentHashMap的原理 ,serialVersionUID常量,redis单线程,
- The stack size specified is too small, specify at least 328k
- Simple two ball loading
- 北斗通信模块 北斗gps模块 北斗通信终端DTU
- 5. Use of ly tab plug-in of header component
- Update a piece of data from the database. Will CDC get two pieces of data with OP fields D and C at the same time? I remember before, only OP was U
- 单工,半双工,全双工区别以及TDD和FDD区别
- Three questions about scientific entrepreneurship: timing, pain points and important decisions
- 啟動solr報錯The stack size specified is too small,Specify at least 328k
猜你喜欢
Example code of second kill based on MySQL optimistic lock
MySQL报错1040Too many connections的原因以及解决方案

一款Flutter版的记事本

Fiori applications are shared through the enhancement of adaptation project

Computer network interview knowledge points

mysql统计账单信息(下):数据导入及查询

8 popular recommended style layout

ROS2 Foxy depthai_ ROS tutorial

spark源码(五)DAGScheduler TaskScheduler如何配合提交任务,application、job、stage、taskset、task对应关系是什么?

Professor Li Zexiang, Hong Kong University of science and technology: I'm wrong. Why is engineering consciousness more important than the best university?
随机推荐
IO的几种模型 阻塞,非阻塞,io多路复用,信号驱动和异步io
minimum spanning tree
微机原理与接口技术知识点整理复习–纯手打
Leetcode第一题:两数之和(3种语言)
1553B environment construction
内容审计技术
Camp division of common PLC programming software
机器学习—性能度量
8 popular recommended style layout
机器学习总结(一):线性回归、岭回归、Lasso回归
Jenkins+webhooks- multi branch parametric construction-
Asp. NETCORE uses dynamic to simplify database access
Vs code set code auto save
A Fletter version of Notepad
Report on the 14th five year plan and future development trend of China's integrated circuit packaging industry Ⓓ 2022 ~ 2028
1.8新特性-List
spark源码阅读总纲
单工,半双工,全双工区别以及TDD和FDD区别
硬件开发笔记(九): 硬件开发基本流程,制作一个USB转RS232的模块(八):创建asm1117-3.3V封装库并关联原理图元器件
【机器学习】VAE变分自编码器学习笔记