初识 BPF:从 Hello World 开始的内核编程之旅

初识 BPF:从 Hello World 开始的内核编程之旅

Part 1 概述

1.背景

BPF 技术被列为近些年 Linux 内核领域最火热的新领域之一。它成功的给 Linux 内核赋予了少量的动态可编程性,可以在 Linux 内核运行时,实时修改内核的行为,但不需要重新编译和重启内核。据此,BPF 在 Linux 世界中:

  • 网络
  • 可观测性
  • 安全

三大领域大放异彩,学习好 BPF 技术,对于 Linux 内核和应用开发者来说,是一件非常有意义的事情。

www.zeeklog.com - 初识 BPF:从 Hello World 开始的内核编程之旅

2. 什么是 BPF?

BPF 在 Linux 内核中,被实现为一个非常精简的虚拟机,具有几乎性能无损的执行效率。我们可以用类似 C 语言的语法,编写代码,编译成可以在 BPF 虚拟机中运行的汇编代码,实现其强大的逻辑处理能力。

今天,我们将从 Hello World 开始,带您进入 BPF 的世界。

3. 开始 BPF

按照计算机程序设计语言学习的传统,我们从经典的 Hello World 程序开始我们的 eBPF 开发之旅。首先简单对比下标准 C 程序和 eBPF 程序的异同。

C 语言程序

# HelloWorld.c

#include <stdio.h> int main() { printf("HelloWorld\n"); return 0; }

# C 语言的编译与运行

www.zeeklog.com - 初识 BPF:从 Hello World 开始的内核编程之旅

eBPF 程序

# HelloWorld.bpf.c

#include <linux/bpf.h> #include <bpf/bpf_helpers.h> int helloworld(void *ctx) { bpf_printk("Hello world!\n"); return 0; }

# eBPF 程序的编译与运行

eBPF 的编译、加载、运行比标准 C 程序要麻烦一些。接下来会一步一步介绍全流程。

Part 2 开发环境

我们使用 vagrant + virtualbox +Ubuntu22.04 组建我们的开发环境。注意,只支持 X86 的 CPU (Windows, Linux 或 Mac)。苹果 M 芯片不支持。

保存虚拟机描述文件 Vagrantfile:

Vagrant.configure('2') do |config| config.vm.define 'bpf' do |bpf| bpf.vm.provider 'virtualbox' do |vb| vb.gui = false vb.memory = '4096' vb.cpus = 2 end bpf.vm.synced_folder '.', '/vagrant', disabled: true bpf.vm.box = 'bento/ubuntu-22.04' bpf.vm.network 'private_network', type: 'dhcp' bpf.vm.provision 'shell', inline: <<-SHELL set -x set -e apt-get install -y linux-tools-generic linux-tools-$(uname -r) gcc-multilib clang libbpf0 libbpf-dev SHELL end end

一些常用命令:

  • vagrant up: 启动虚拟机
  • vagrant halt: 关闭虚拟机电源
  • vagrant detroy: 销毁虚拟机
  • vagrant ssh: 登入虚拟机

Part 3 开始开发


以上文提到的 HelloWorld.bpf.c 为例子。

1. 编译 eBPF

生成 eBPF 字节码,并带有调试信息:

clang -target bpf -Wall -O2 -g -c HelloWorld.bpf.c -o HelloWorld.o

查看 eBPF 编译后的字节码:

llvm-objdump -d -r -S --print-imm-hex HelloWorld.o
root@vagrant:~# llvm-objdump -d -r -S --print-imm-hex HelloWorld.o HelloWorld.o: file format elf64-bpf Disassembly of section .text: 0000000000000000 <helloworld>: ; { 0: b7 01 00 00 0a 00 00 00 r1 = 0xa ; bpf_printk("Hello world!\n"); 1: 6b 1a fc ff 00 00 00 00 *(u16*)(r10 - 0x4) = r1 2: b7 01 00 00 72 6c 64 21 r1 = 0x21646c72 3: 63 1a f8 ff 00 00 00 00 *(u32*)(r10 - 0x8) = r1 4: 18 01 00 00 48 65 6c 6c 00 00 00 00 6f 20 77 6f r1 = 0x6f77206f6c6c6548 ll 6: 7b 1a f0 ff 00 00 00 00 *(u64*)(r10 - 0x10) = r1 7: bf a1 00 00 00 00 00 00 r1 = r10 8: 07 01 00 00 f0 ff ff ff r1 += -0x10 ; bpf_printk("Hello world!\n"); 9: b7 02 00 00 0e 00 00 00 r2 = 0xe 10: 85 00 00 00 06 00 00 00 call 0x6 ; return 0; 11: b7 00 00 00 00 00 00 00 r0 = 0x0 12: 95 00 00 00 00 00 00 00 exit

2. 运行 BPF

流程

www.zeeklog.com - 初识 BPF:从 Hello World 开始的内核编程之旅

Linux 内核中支持 eBPF 的事件很多。挂载 eBPF 程序后,需要等待事件异步触发才能看到效果。

加载 eBPF 字节码

Linux 内核官方工具 bpftool 帮我们封装了对 eBPF 字节码各种操作。本文我们用它来挂载和调试 eBPF。

# 在内核生成 eBPF 文件结构

eBPF 在 Linux 内核以特殊文件形式存在,如果进程退出了则会自动销毁。为了让 eBPF 程序独立于进程持久存在于 Linux 内核中,内核提供了 bpf 文件系统,可以把 eBPF 程序 pin 在其中。Ubuntu 22.04 默认挂载了 bpf 文件系统,位于 /sys/fs/bpf,可以直接使用。

现在的 eBPF 字节码只是一段单纯的代码,要把它传入内核,还需要指定该 eBPF 字节码的类型,可以通过 bpftool feature 列出内核支持的所有 eBPF 类型:

 ... Scanning eBPF program types... eBPF program_type socket_filter is available eBPF program_type kprobe is available eBPF program_type sched_cls is available eBPF program_type sched_act is available eBPF program_type tracepoint is available eBPF program_type xdp is available eBPF program_type perf_event is available eBPF program_type cgroup_skb is available eBPF program_type cgroup_sock is available eBPF program_type lwt_in is available eBPF program_type lwt_out is available ...

HelloWorld.bpf.c 是一个 "万能" 的 eBPF 程序,因为它仅仅输出 "HelloWorld",因此,它可以指定为任何 eBPF 程序类型,这里我们选用 raw_tracepoint。

bpftool prog load HelloWorld.o /sys/fs/bpf/HelloWorld type raw_tracepoint

现在可以在 /sys/fs/bpf/ 中看到:

root@vagrant:~# ls /sys/fs/bpf/HelloWorld /sys/fs/bpf/HelloWorld

或者:

root@vagrant:~# bpftool prog show pinned /sys/fs/bpf/HelloWorld 353: raw_tracepoint name helloworld tag fc3c56cde923df12 gpl loaded_at 2023-03-11T01:59:48+0000 uid 0 xlated 104B jited 71B memlock 4096B btf_id 118

这说明我们的 eBPF 程序已经成功加载到内核中。

#把内核中的 eBPF 程序挂载到事件上

正常情况下,我们会把 eBPF 挂载到特定事件上,然后等待事件异步触发时,再执行 eBPF 程序。

本文我们为了方便,选择最简单的,用于调试和测试用的接口:BPF_PROG_TEST_RUN,也叫 BPF_PROG_RUN,

他们是完全等价的。这个接口可以直接同步执行 eBPF 程序,而不需要挂载到事件上再等待事件异步发生。

BPF_PROG_TEST_RUN 只支持有限的 eBPF 程序类型:

BPF_PROG_TYPE_SOCKET_FILTER BPF_PROG_TYPE_SCHED_CLS BPF_PROG_TYPE_SCHED_ACT BPF_PROG_TYPE_XDP BPF_PROG_TYPE_SK_LOOKUP BPF_PROG_TYPE_CGROUP_SKB BPF_PROG_TYPE_LWT_IN BPF_PROG_TYPE_LWT_OUT BPF_PROG_TYPE_LWT_XMIT BPF_PROG_TYPE_LWT_SEG6LOCAL BPF_PROG_TYPE_FLOW_DISSECTOR BPF_PROG_TYPE_STRUCT_OPS BPF_PROG_TYPE_RAW_TRACEPOINT BPF_PROG_TYPE_SYSCALL

上文我们把 eBPF 字节码指定为 raw_tracepoint,正好满足该接口的要求。

利用 bpftool 使用 BPF_PROG_TEST_RUN 接口来运行它 (参数如何设置,之后会有专题分析,这里不要修改 bpftool 参数):

bpftool prog run pinned /sys/fs/bpf/HelloWorld repeat 0

查看输出结果:

root@vagrant:~# cat /sys/kernel/debug/tracing/trace # tracer: nop # # entries-in-buffer/entries-written: 1/1 #P:4 # # _-----=> irqs-off # / _----=> need-resched # | / _---=> hardirq/softirq # || / _--=> preempt-depth # ||| / _-=> migrate-disable # |||| / delay # TASK-PID CPU# ||||| TIMESTAMP FUNCTION # | | | ||||| | | bpftool-39498 [001] d.... 979047.864950: bpf_trace_printk: Hello world!

OK, 我们现在成功的执行了第一个最简单的 BPF 程序。

Part 4 BPF in MatrixOne

MatrixOne 作为我司主打数据库产品,未来在 MatrixOne 中将全面使用 BPF 技术,用于增强数据库集群的性能,稳定性和安全性。

网络

MatrixOne 作为云原生数据库,运行在标准的 Kubernetes 集群中,网络是其最重要的基础设施之一。我们将利用 BPF 技术,减少 Linux 内核网络协议栈的开销,实现 MatrixOne 的网络性能优化。

可观测性

我们将在自带的可观测性组件中,利用 BPF 实时采集和分析数据库运行时的各种指标,用于自动化分析和故障诊断。同时,我们将提供一系列 BPF 工具,用于 SRE 人工分析和调优 MatrixOne。

安全性

我们将在附属的安全组件中,提供基于 BPF 得关键操作监控和禁止操作,用于实时检测和防御数据库的安全威胁。

关于 MatrixOne

MatrixOne 是一款基于云原生技术,可同时在公有云和私有云部署的多模数据库。该产品使用存算分离、读写分离、冷热分离的原创技术架构,能够在一套存储和计算系统下同时支持事务、分析、流、时序和向量等多种负载,并能够实时、按需的隔离或共享存储和计算资源。云原生数据库 MatrixOne 能够帮助用户大幅简化日益复杂的 IT 架构,提供极简、极灵活、高性价比和高性能的数据服务。

MatrixOne 企业版和 MatrixOne 云服务自发布以来,已经在互联网、金融、能源、制造、教育、医疗等多个行业得到应用。得益于其独特的架构设计,用户可以降低多达 70% 的硬件和运维成本,增加 3-5 倍的开发效率,同时更加灵活的响应市场需求变化和更加高效的抓住创新机会。在相同硬件投入时,MatrixOne 可获得数倍以上的性能提升。

MatrixOne 秉持开源开放、生态共建的理念,核心代码全部开源,全面兼容 MySQL 协议,并与合作伙伴打造了多个端到端解决方案,大幅降低用户的迁移和使用成本,也帮助用户避免了供应商锁定风险。

Read more

【OpenClaw从入门到精通】第10篇:OpenClaw生产环境部署全攻略:性能优化+安全加固+监控运维(2026实测版)

【OpenClaw从入门到精通】第10篇:OpenClaw生产环境部署全攻略:性能优化+安全加固+监控运维(2026实测版)

摘要:本文聚焦OpenClaw从测试环境走向生产环境的核心痛点,围绕“性能优化、安全加固、监控运维”三大维度展开实操讲解。先明确生产环境硬件/系统选型标准,再通过硬件层资源管控、模型调度策略、缓存优化等手段提升响应速度(实测响应效率提升50%+);接着从网络、权限、数据三层构建安全防护体系,集成火山引擎安全方案拦截高危操作;最后落地TenacitOS可视化监控与Prometheus告警体系,配套完整故障排查清单和虚拟实战案例。全文所有配置、代码均经实测验证,兼顾新手入门实操性和进阶读者的生产级部署需求,帮助开发者真正实现OpenClaw从“能用”到“放心用”的跨越。 优质专栏欢迎订阅! 【DeepSeek深度应用】【Python高阶开发:AI自动化与数据工程实战】【YOLOv11工业级实战】 【机器视觉:C# + HALCON】【大模型微调实战:平民级微调技术全解】 【人工智能之深度学习】【AI 赋能:Python 人工智能应用实战】【数字孪生与仿真技术实战指南】 【AI工程化落地与YOLOv8/v9实战】【C#工业上位机高级应用:高并发通信+性能优化】 【Java生产级避坑指南:

By Ne0inhk
ARM Linux 驱动开发篇--- Linux 并发与竞争实验(互斥体实现 LED 设备互斥访问)--- Ubuntu20.04互斥体实验

ARM Linux 驱动开发篇--- Linux 并发与竞争实验(互斥体实现 LED 设备互斥访问)--- Ubuntu20.04互斥体实验

🎬 渡水无言:个人主页渡水无言 ❄专栏传送门: 《linux专栏》《嵌入式linux驱动开发》《linux系统移植专栏》 ❄专栏传送门: 《freertos专栏》《STM32 HAL库专栏》 ⭐️流水不争先,争的是滔滔不绝  📚博主简介:第二十届中国研究生电子设计竞赛全国二等奖 |国家奖学金 | 省级三好学生 | 省级优秀毕业生获得者 | ZEEKLOG新星杯TOP18 | 半导纵横专栏博主 | 211在读研究生 在这里主要分享自己学习的linux嵌入式领域知识;有分享错误或者不足的地方欢迎大佬指导,也欢迎各位大佬互相三连 目录 前言  一、实验基础说明 1.1、互斥体简介 1.2 本次实验设计思路 二、硬件原理分析(看过之前博客的可以忽略) 三、实验程序编写 3.1 互斥体 LED 驱动代码(mutex.c) 3.2.1、设备结构体定义(28-39

By Ne0inhk
Flutter for OpenHarmony:swagger_dart_code_generator 接口代码自动化生成的救星(OpenAPI/Swagger) 深度解析与鸿蒙适配指南

Flutter for OpenHarmony:swagger_dart_code_generator 接口代码自动化生成的救星(OpenAPI/Swagger) 深度解析与鸿蒙适配指南

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.ZEEKLOG.net 前言 后端工程师扔给你一个 Swagger (OpenAPI) 文档地址,你会怎么做? 1. 对着文档,手写 Dart Model 类(容易写错字段类型)。 2. 手写 Retrofit/Dio 的 API 接口定义(容易拼错 URL)。 3. 当后端修改了字段名,你对着报错修半天。 这是重复劳动的地狱。 swagger_dart_code_generator 可以将 Swagger (JSON/YAML) 文件直接转换为高质量的 Dart 代码,包括: * Model 类:支持 json_serializable,带 fromJson/

By Ne0inhk
Linux 开发别再卡壳!makefile/git/gdb 全流程实操 + 作业解析,新手看完直接用----《Hello Linux!》(5)

Linux 开发别再卡壳!makefile/git/gdb 全流程实操 + 作业解析,新手看完直接用----《Hello Linux!》(5)

文章目录 * 前言 * make/makefile * 文件的三个时间 * Linux第一个小程序-进度条 * 回车和换行 * 缓冲区 * 程序的代码展示 * git指令 * 关于gitee * Linux调试器-gdb使用 * 作业部分 前言 做 Linux 开发时,你是不是也遇到过这些 “卡脖子” 时刻?写 makefile 时,明明语法没错却报错,最后发现是依赖方法行没加 Tab;想提交代码到 gitee,记不清 git add/commit/push 的 “三板斧”,还得反复搜教程;用 gdb 调试程序,输了命令没反应,才想起编译时没加-g生成 debug 版本;甚至连写个进度条,都搞不懂\r和\n的区别,导致进度条乱跳…… 其实这些问题,

By Ne0inhk