Files
bpf-developer-tutorial/1-helloworld/index.html

378 lines
33 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE HTML>
<html lang="en" class="light" dir="ltr">
<head>
<!-- Book generated using mdBook -->
<meta charset="UTF-8">
<title>lesson 1-helloworld - bpf-developer-tutorial</title>
<!-- Custom HTML head -->
<meta name="description" content="">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="theme-color" content="#ffffff">
<link rel="icon" href="../favicon.svg">
<link rel="shortcut icon" href="../favicon.png">
<link rel="stylesheet" href="../css/variables.css">
<link rel="stylesheet" href="../css/general.css">
<link rel="stylesheet" href="../css/chrome.css">
<link rel="stylesheet" href="../css/print.css" media="print">
<!-- Fonts -->
<link rel="stylesheet" href="../FontAwesome/css/font-awesome.css">
<link rel="stylesheet" href="../fonts/fonts.css">
<!-- Highlight.js Stylesheets -->
<link rel="stylesheet" href="../highlight.css">
<link rel="stylesheet" href="../tomorrow-night.css">
<link rel="stylesheet" href="../ayu-highlight.css">
<!-- Custom theme stylesheets -->
</head>
<body class="sidebar-visible no-js">
<div id="body-container">
<!-- Provide site root to javascript -->
<script>
var path_to_root = "../";
var default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "navy" : "light";
</script>
<!-- Work around some values being stored in localStorage wrapped in quotes -->
<script>
try {
var theme = localStorage.getItem('mdbook-theme');
var sidebar = localStorage.getItem('mdbook-sidebar');
if (theme.startsWith('"') && theme.endsWith('"')) {
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
}
if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
}
} catch (e) { }
</script>
<!-- Set the theme before any content is loaded, prevents flash -->
<script>
var theme;
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
if (theme === null || theme === undefined) { theme = default_theme; }
var html = document.querySelector('html');
html.classList.remove('light')
html.classList.add(theme);
var body = document.querySelector('body');
body.classList.remove('no-js')
body.classList.add('js');
</script>
<input type="checkbox" id="sidebar-toggle-anchor" class="hidden">
<!-- Hide / unhide sidebar before it is displayed -->
<script>
var body = document.querySelector('body');
var sidebar = null;
var sidebar_toggle = document.getElementById("sidebar-toggle-anchor");
if (document.body.clientWidth >= 1080) {
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
sidebar = sidebar || 'visible';
} else {
sidebar = 'hidden';
}
sidebar_toggle.checked = sidebar === 'visible';
body.classList.remove('sidebar-visible');
body.classList.add("sidebar-" + sidebar);
</script>
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
<div class="sidebar-scrollbox">
<ol class="chapter"><li class="chapter-item expanded affix "><a href="../https://github.com/eunomia-bpf/bpf-developer-tutorial.html">https://github.com/eunomia-bpf/bpf-developer-tutorial</a></li><li class="chapter-item expanded affix "><li class="part-title">入门文档</li><li class="chapter-item expanded "><a href="../0-introduce/index.html"><strong aria-hidden="true">1.</strong> lesson 0-introduce</a></li><li class="chapter-item expanded "><a href="../1-helloworld/index.html" class="active"><strong aria-hidden="true">2.</strong> lesson 1-helloworld</a></li><li class="chapter-item expanded "><a href="../2-kprobe-unlink/index.html"><strong aria-hidden="true">3.</strong> lesson 2-kprobe-unlink</a></li><li class="chapter-item expanded "><a href="../3-fentry-unlink/index.html"><strong aria-hidden="true">4.</strong> lesson 3-fentry-unlink</a></li><li class="chapter-item expanded "><a href="../4-opensnoop/index.html"><strong aria-hidden="true">5.</strong> lesson 4-opensnoop</a></li><li class="chapter-item expanded "><a href="../5-uprobe-bashreadline/index.html"><strong aria-hidden="true">6.</strong> lesson 5-uprobe-bashreadline</a></li><li class="chapter-item expanded "><a href="../6-sigsnoop/index.html"><strong aria-hidden="true">7.</strong> lesson 6-sigsnoop</a></li><li class="chapter-item expanded "><a href="../7-execsnoop/index.html"><strong aria-hidden="true">8.</strong> lesson 7-execsnoop</a></li><li class="chapter-item expanded "><a href="../8-exitsnoop/index.html"><strong aria-hidden="true">9.</strong> lesson 8-execsnoop</a></li><li class="chapter-item expanded "><a href="../9-runqlat/index.html"><strong aria-hidden="true">10.</strong> lesson 9-runqlat</a></li><li class="chapter-item expanded "><a href="../10-hardirqs/index.html"><strong aria-hidden="true">11.</strong> lesson 10-hardirqs</a></li><li class="chapter-item expanded affix "><li class="part-title">进阶文档和示例</li><li class="chapter-item expanded "><a href="../11-bootstrap/index.html"><strong aria-hidden="true">12.</strong> lesson 11-bootstrap</a></li><li class="chapter-item expanded "><a href="../12-profile/index.html"><strong aria-hidden="true">13.</strong> lesson 12-profile</a></li><li class="chapter-item expanded "><a href="../13-tcpconnlat/index.html"><strong aria-hidden="true">14.</strong> lesson 13-tcpconnlat</a></li><li class="chapter-item expanded "><a href="../14-tcpstates/index.html"><strong aria-hidden="true">15.</strong> lesson 14-tcpstates</a></li><li class="chapter-item expanded "><a href="../15-javagc/index.html"><strong aria-hidden="true">16.</strong> lesson 15-javagc</a></li><li class="chapter-item expanded "><a href="../16-memleak/index.html"><strong aria-hidden="true">17.</strong> lesson 16-memleak</a></li><li class="chapter-item expanded "><a href="../17-biopattern/index.html"><strong aria-hidden="true">18.</strong> lesson 17-biopattern</a></li><li class="chapter-item expanded "><a href="../18-further-reading/index.html"><strong aria-hidden="true">19.</strong> lesson 18-further-reading</a></li><li class="chapter-item expanded "><a href="../19-lsm-connect/index.html"><strong aria-hidden="true">20.</strong> lesson 19-lsm-connect</a></li><li class="chapter-item expanded "><a href="../20-tc/index.html"><strong aria-hidden="true">21.</strong> lesson 20-tc</a></li><li class="chapter-item expanded "><a href="../21-xdp/index.html"><strong aria-hidden="true">22.</strong> lesson 21-xdp</a></li><li class="chapter-item expanded affix "><li class="part-title">高级主题</li><li class="chapter-item expanded "><a href="../22-android/index.html"><strong aria-hidden="true">23.</strong> 在 Android 上使用 eBPF 程序</a></li><li class="chapter-item expanded "><a href="../30-sslsniff/index.html"><strong aria-hidden="true">24.</strong> 使用 uprobe 捕获多种库的 SSL/TLS 明文数据</a></li><li class="chapter-item expanded "><a href="../23-http/index.html"><strong aria-hidden="true">25.</strong> 使用 eBPF socket filter 或 syscall trace 追踪 HTTP 请求和其他七层协议</a></li><li class="chapter-item expanded "><a href="../29-sockops/index.html"><strong aria-hidden="true">26.</strong> 使用 sockops 加速网络请求转发</a></li><li class="chapter-item expanded "><a href="../24-hide/index.html"><strong aria-hidden="true">27.</strong> 使用 eBPF 隐藏进程或文件信息</a></li><li class="chapter-item expanded "><a href="../25-signal/index.html"><strong aria-hidden="true">28.</strong> 使用 bpf_send_signal 发送信号终止进程</a></li><li class="chapter-item expanded "><a href="../26-sudo/index.html"><strong aria-hidden="true">29.</strong> 使用 eBPF 添加 sudo 用户</a></li><li class="chapter-item expanded "><a href="../27-replace/index.html"><strong aria-hidden="true">30.</strong> 使用 eBPF 替换任意程序读取或写入的文本</a></li><li class="chapter-item expanded "><a href="../28-detach/index.html"><strong aria-hidden="true">31.</strong> BPF 的生命周期:使用 Detached 模式在用户态应用退出后持续运行 eBPF 程序</a></li><li class="chapter-item expanded "><a href="../18-further-reading/ebpf-security.zh.html"><strong aria-hidden="true">32.</strong> eBPF 运行时的安全性与面临的挑战</a></li><li class="chapter-item expanded "><a href="../34-syscall/index.html"><strong aria-hidden="true">33.</strong> 使用 eBPF 修改系统调用参数</a></li><li class="chapter-item expanded "><a href="../35-user-ringbuf/index.html"><strong aria-hidden="true">34.</strong> eBPF开发实践使用 user ring buffer 向内核异步发送信息</a></li><li class="chapter-item expanded "><a href="../36-userspace-ebpf/index.html"><strong aria-hidden="true">35.</strong> 用户空间 eBPF 运行时:深度解析与应用实践</a></li><li class="chapter-item expanded "><a href="../37-uprobe-rust/index.html"><strong aria-hidden="true">36.</strong> 使用 uprobe 追踪 Rust 应用程序</a></li><li class="chapter-item expanded "><a href="../38-btf-uprobe/index.html"><strong aria-hidden="true">37.</strong> 借助 eBPF 和 BTF让用户态也能一次编译、到处运行</a></li><li class="chapter-item expanded affix "><li class="part-title">bcc 和 bpftrace 教程与文档</li><li class="chapter-item expanded "><a href="../bcc-documents/kernel-versions.html"><strong aria-hidden="true">38.</strong> BPF Features by Linux Kernel Version</a></li><li class="chapter-item expanded "><a href="../bcc-documents/kernel_config.html"><strong aria-hidden="true">39.</strong> Kernel Configuration for BPF Features</a></li><li class="chapter-item expanded "><a href="../bcc-documents/reference_guide.html"><strong aria-hidden="true">40.</strong> bcc Reference Guide</a></li><li class="chapter-item expanded "><a href="../bcc-documents/special_filtering.html"><strong aria-hidden="true">41.</strong> Special Filtering</a></li><li class="chapter-item expanded "><a href="../bcc-documents/tutorial.html"><strong aria-hidden="true">42.</strong> bcc Tutorial</a></li><li class="chapter-item expanded "><a href="../bcc-documents/tutorial_bcc_python_developer.html"><strong aria-hidden="true">43.</strong> bcc Python Developer Tutorial</a></li><li class="chapter-item expanded "><a href="../bpftrace-tutorial/index.html"><strong aria-hidden="true">44.</strong> bpftrace Tutorial</a></li></ol>
</div>
<div id="sidebar-resize-handle" class="sidebar-resize-handle">
<div class="sidebar-resize-indicator"></div>
</div>
</nav>
<!-- Track and set sidebar scroll position -->
<script>
var sidebarScrollbox = document.querySelector('#sidebar .sidebar-scrollbox');
sidebarScrollbox.addEventListener('click', function(e) {
if (e.target.tagName === 'A') {
sessionStorage.setItem('sidebar-scroll', sidebarScrollbox.scrollTop);
}
}, { passive: true });
var sidebarScrollTop = sessionStorage.getItem('sidebar-scroll');
sessionStorage.removeItem('sidebar-scroll');
if (sidebarScrollTop) {
// preserve sidebar scroll position when navigating via links within sidebar
sidebarScrollbox.scrollTop = sidebarScrollTop;
} else {
// scroll sidebar to current active section when navigating via "next/previous chapter" buttons
var activeSection = document.querySelector('#sidebar .active');
if (activeSection) {
activeSection.scrollIntoView({ block: 'center' });
}
}
</script>
<div id="page-wrapper" class="page-wrapper">
<div class="page">
<div id="menu-bar-hover-placeholder"></div>
<div id="menu-bar" class="menu-bar sticky">
<div class="left-buttons">
<label id="sidebar-toggle" class="icon-button" for="sidebar-toggle-anchor" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
<i class="fa fa-bars"></i>
</label>
<button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
<i class="fa fa-paint-brush"></i>
</button>
<ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
<li role="none"><button role="menuitem" class="theme" id="light">Light</button></li>
<li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
<li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
<li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
<li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
</ul>
<button id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
<i class="fa fa-search"></i>
</button>
</div>
<h1 class="menu-title">bpf-developer-tutorial</h1>
<div class="right-buttons">
<a href="../print.html" title="Print this book" aria-label="Print this book">
<i id="print-button" class="fa fa-print"></i>
</a>
</div>
</div>
<div id="search-wrapper" class="hidden">
<form id="searchbar-outer" class="searchbar-outer">
<input type="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
</form>
<div id="searchresults-outer" class="searchresults-outer hidden">
<div id="searchresults-header" class="searchresults-header"></div>
<ul id="searchresults">
</ul>
</div>
</div>
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
<script>
document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
});
</script>
<div id="content" class="content">
<main>
<h1 id="ebpf-入门开发实践教程一hello-world基本框架和开发流程"><a class="header" href="#ebpf-入门开发实践教程一hello-world基本框架和开发流程">eBPF 入门开发实践教程一Hello World基本框架和开发流程</a></h1>
<p>在本篇博客中我们将深入探讨eBPFExtended Berkeley Packet Filter的基本框架和开发流程。eBPF是一种在Linux内核上运行的强大网络和性能分析工具它为开发者提供了在内核运行时动态加载、更新和运行用户定义代码的能力。这使得开发者可以实现高效、安全的内核级别的网络监控、性能分析和故障排查等功能。</p>
<p>本文是eBPF入门开发实践教程的第二篇我们将重点关注如何编写一个简单的eBPF程序并通过实际例子演示整个开发流程。在阅读本教程之前建议您先学习第一篇教程以便对eBPF的基本概念有个大致的了解。</p>
<p>在开发eBPF程序时有多种开发框架可供选择如 BCCBPF Compiler Collectionlibbpf、cilium/ebpf、eunomia-bpf 等。虽然不同工具的特点各异,但它们的基本开发流程大致相同。在接下来的内容中,我们将深入了解这些流程,并以 Hello World 程序为例带领读者逐步掌握eBPF开发的基本技巧。</p>
<p>本教程将帮助您了解eBPF程序的基本结构、编译和加载过程、用户空间与内核空间的交互方式以及调试与优化技巧。通过学习本教程您将掌握eBPF开发的基本知识并为后续进一步学习和实践奠定坚实的基础。</p>
<h2 id="ebpf开发环境准备与基本开发流程"><a class="header" href="#ebpf开发环境准备与基本开发流程">eBPF开发环境准备与基本开发流程</a></h2>
<p>在开始编写eBPF程序之前我们需要准备一个合适的开发环境并了解eBPF程序的基本开发流程。本部分将详细介绍这些内容。</p>
<h3 id="安装必要的软件和工具"><a class="header" href="#安装必要的软件和工具">安装必要的软件和工具</a></h3>
<p>要开发eBPF程序您需要安装以下软件和工具</p>
<ul>
<li>Linux 内核由于eBPF是内核技术因此您需要具备较新版本的Linux内核至少 4.8 及以上版本,建议至少在 5.15 以上以支持eBPF功能。
<ul>
<li>建议使用最新的 Ubuntu 版本(例如 Ubuntu 23.10)以获得最佳的学习体验,较旧的内核 eBPF 功能支持可能相对不全。</li>
</ul>
</li>
<li>LLVM 和 Clang这些工具用于编译eBPF程序。安装最新版本的LLVM和Clang可以确保您获得最佳的eBPF支持。</li>
</ul>
<p>eBPF 程序主要由两部分构成:内核态部分和用户态部分。内核态部分包含 eBPF 程序的实际逻辑,用户态部分负责加载、运行和监控内核态程序。</p>
<p>当您选择了合适的开发框架后如BCCBPF Compiler Collection、libbpf、cilium/ebpf或eunomia-bpf等您可以开始进行用户态和内核态程序的开发。以BCC工具为例我们将介绍eBPF程序的基本开发流程</p>
<ol>
<li>安装BCC工具根据您的Linux发行版按照BCC官方文档的指南安装BCC工具和相关依赖。</li>
<li>编写eBPF程序C语言使用C语言编写一个简单的eBPF程序例如Hello World程序。该程序可以在内核空间执行并完成特定任务如统计网络数据包数量。</li>
<li>编写用户态程序Python或C等使用Python、C等语言编写用户态程序用于加载、运行eBPF程序以及与之交互。在这个程序中您需要使用BCC提供的API来加载和操作内核态的eBPF程序。</li>
<li>编译eBPF程序使用BCC工具将C语言编写的eBPF程序编译成内核可以执行的字节码。BCC会在运行时动态从源码编译eBPF程序。</li>
<li>加载并运行eBPF程序在用户态程序中使用BCC提供的API加载编译好的eBPF程序到内核空间然后运行该程序。</li>
<li>与eBPF程序交互用户态程序通过BCC提供的API与eBPF程序交互实现数据收集、分析和展示等功能。例如您可以使用BCC API读取eBPF程序中的map数据以获取网络数据包统计信息。</li>
<li>卸载eBPF程序当不再需要eBPF程序时用户态程序应使用BCC API将其从内核空间卸载。</li>
<li>调试与优化:使用 bpftool 等工具进行eBPF程序的调试和优化提高程序性能和稳定性。</li>
</ol>
<p>通过以上流程您可以使用BCC工具开发、编译、运行和调试eBPF程序。请注意其他框架如libbpf、cilium/ebpf和eunomia-bpf的开发流程大致相似但略有不同因此在选择框架时请参考相应的官方文档和示例。</p>
<p>通过这个过程,你可以开发出一个能够在内核中运行的 eBPF 程序。eunomia-bpf 是一个开源的 eBPF 动态加载运行时和开发工具链,它的目的是简化 eBPF 程序的开发、构建、分发、运行。它基于 libbpf 的 CO-RE 轻量级开发框架,支持通过用户态 WASM 虚拟机控制 eBPF 程序的加载和执行,并将预编译的 eBPF 程序打包为通用的 JSON 或 WASM 模块进行分发。我们会使用 eunomia-bpf 进行演示。</p>
<h2 id="下载安装-eunomia-bpf-开发工具"><a class="header" href="#下载安装-eunomia-bpf-开发工具">下载安装 eunomia-bpf 开发工具</a></h2>
<p>可以通过以下步骤下载和安装 eunomia-bpf</p>
<p>下载 ecli 工具,用于运行 eBPF 程序:</p>
<pre><code class="language-console">$ wget https://aka.pw/bpf-ecli -O ecli &amp;&amp; chmod +x ./ecli
$ ./ecli -h
Usage: ecli [--help] [--version] [--json] [--no-cache] url-and-args
</code></pre>
<p>下载编译器工具链,用于将 eBPF 内核代码编译为 config 文件或 WASM 模块:</p>
<pre><code class="language-console">$ wget https://github.com/eunomia-bpf/eunomia-bpf/releases/latest/download/ecc &amp;&amp; chmod +x ./ecc
$ ./ecc -h
eunomia-bpf compiler
Usage: ecc [OPTIONS] &lt;SOURCE_PATH&gt; [EXPORT_EVENT_HEADER]
</code></pre>
<p>注:假如在 aarch64 平台上,请从 release 下载 <a href="https://github.com/eunomia-bpf/eunomia-bpf/releases/latest/download/ecc-aarch64">ecc-aarch64</a><a href="https://github.com/eunomia-bpf/eunomia-bpf/releases/latest/download/ecli-aarch64">ecli-aarch64</a>.</p>
<p>也可以使用 docker 镜像进行编译:</p>
<pre><code class="language-console">$ docker run -it -v `pwd`/:/src/ ghcr.io/eunomia-bpf/ecc-`uname -m`:latest # 使用 docker 进行编译。`pwd` 应该包含 *.bpf.c 文件和 *.h 文件。
export PATH=PATH:~/.eunomia/bin
Compiling bpf object...
Packing ebpf object and config into /src/package.json...
</code></pre>
<h2 id="hello-world---minimal-ebpf-program"><a class="header" href="#hello-world---minimal-ebpf-program">Hello World - minimal eBPF program</a></h2>
<p>我们会先从一个简单的 eBPF 程序开始,它会在内核中打印一条消息。我们会使用 eunomia-bpf 的编译器工具链将其编译为 bpf 字节码文件,然后使用 ecli 工具加载并运行该程序。作为示例,我们可以暂时省略用户态程序的部分。</p>
<pre><code class="language-c">/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
#define BPF_NO_GLOBAL_DATA
#include &lt;linux/bpf.h&gt;
#include &lt;bpf/bpf_helpers.h&gt;
#include &lt;bpf/bpf_tracing.h&gt;
typedef unsigned int u32;
typedef int pid_t;
const pid_t pid_filter = 0;
char LICENSE[] SEC("license") = "Dual BSD/GPL";
SEC("tp/syscalls/sys_enter_write")
int handle_tp(void *ctx)
{
pid_t pid = bpf_get_current_pid_tgid() &gt;&gt; 32;
if (pid_filter &amp;&amp; pid != pid_filter)
return 0;
bpf_printk("BPF triggered sys_enter_write from PID %d.\n", pid);
return 0;
}
</code></pre>
<p>这段程序通过定义一个 handle_tp 函数并使用 SEC 宏把它附加到 sys_enter_write tracepoint即在进入 write 系统调用时执行)。该函数通过使用 bpf_get_current_pid_tgid 和 bpf_printk 函数获取调用 write 系统调用的进程 ID并在内核日志中打印出来。</p>
<ul>
<li><code>bpf_printk()</code> 一种将信息输出到trace_pipe(/sys/kernel/debug/tracing/trace_pipe)简单机制。 在一些简单用例中这样使用没有问题, but它也有一些限制最多3 参数; 第一个参数必须是%s(即字符串)同时trace_pipe在内核中全局共享其他并行使用trace_pipe的程序有可能会将 trace_pipe 的输出扰乱。 一个更好的方式是通过 BPF_PERF_OUTPUT(), 稍后将会讲到。</li>
<li><code>void *ctx</code>ctx本来是具体类型的参数 但是由于我们这里没有使用这个参数因此就将其写成void *类型。</li>
<li><code>return 0</code>;必须这样返回0 (如果要知道why, 参考 #139 <a href="https://github.com/iovisor/bcc/issues/139">https://github.com/iovisor/bcc/issues/139</a>)。</li>
</ul>
<p>要编译和运行这段程序,可以使用 ecc 工具和 ecli 命令。首先在 Ubuntu/Debian 上,执行以下命令:</p>
<pre><code class="language-shell">sudo apt install clang llvm
</code></pre>
<p>使用 ecc 编译程序:</p>
<pre><code class="language-console">$ ./ecc minimal.bpf.c
Compiling bpf object...
Packing ebpf object and config into package.json...
</code></pre>
<p>或使用 docker 镜像进行编译:</p>
<pre><code class="language-shell">docker run -it -v `pwd`/:/src/ ghcr.io/eunomia-bpf/ecc-`uname -m`:latest
</code></pre>
<p>然后使用 ecli 运行编译后的程序:</p>
<pre><code class="language-console">$ sudo ./ecli run package.json
Runing eBPF program...
</code></pre>
<p>运行这段程序后,可以通过查看 /sys/kernel/debug/tracing/trace_pipe 文件来查看 eBPF 程序的输出:</p>
<pre><code class="language-console">$ sudo cat /sys/kernel/debug/tracing/trace_pipe | grep "BPF triggered sys_enter_write"
&lt;...&gt;-3840345 [010] d... 3220701.101143: bpf_trace_printk: write system call from PID 3840345.
&lt;...&gt;-3840345 [010] d... 3220701.101143: bpf_trace_printk: write system call from PID 3840345.
</code></pre>
<p>按 Ctrl+C 停止 ecli 进程之后,可以看到对应的输出也停止。</p>
<p>注意:如果正在使用的 Linux 发行版(例如 Ubuntu )默认情况下没有启用跟踪子系统可能看不到任何输出,使用以下指令打开这个功能:</p>
<pre><code class="language-console">$ sudo su
# echo 1 &gt; /sys/kernel/debug/tracing/tracing_on
</code></pre>
<h2 id="ebpf-程序的基本框架"><a class="header" href="#ebpf-程序的基本框架">eBPF 程序的基本框架</a></h2>
<p>如上所述, eBPF 程序的基本框架包括:</p>
<ul>
<li>包含头文件:需要包含 &lt;linux/bpf.h&gt;&lt;bpf/bpf_helpers.h&gt; 等头文件。</li>
<li>定义许可证:需要定义许可证,通常使用 "Dual BSD/GPL"。</li>
<li>定义 BPF 函数:需要定义一个 BPF 函数,例如其名称为 handle_tp其参数为 void *ctx返回值为 int。通常用 C 语言编写。</li>
<li>使用 BPF 助手函数:在例如 BPF 函数中,可以使用 BPF 助手函数 bpf_get_current_pid_tgid() 和 bpf_printk()。</li>
<li>返回值</li>
</ul>
<h2 id="tracepoints"><a class="header" href="#tracepoints">tracepoints</a></h2>
<p>跟踪点tracepoints是内核静态插桩技术在技术上只是放置在内核源代码中的跟踪函数实际上就是在源码中插入的一些带有控制条件的探测点这些探测点允许事后再添加处理函数。比如在内核中最常见的静态跟踪方法就是 printk即输出日志。又比如在系统调用、调度程序事件、文件系统操作和磁盘 I/O 的开始和结束时都有跟踪点。跟踪点于 2009 年在 Linux 2.6.32 版本中首次提供。跟踪点是一种稳定的 API数量有限。</p>
<h2 id="github-模板轻松构建-ebpf-项目和开发环境"><a class="header" href="#github-模板轻松构建-ebpf-项目和开发环境">GitHub 模板:轻松构建 eBPF 项目和开发环境</a></h2>
<p>面对创建一个 eBPF 项目,您是否对如何开始搭建环境以及选择编程语言感到困惑?别担心,我们为您准备了一系列 GitHub 模板以便您快速启动一个全新的eBPF项目。只需在GitHub上点击 <code>Use this template</code> 按钮,即可开始使用。</p>
<ul>
<li><a href="https://github.com/eunomia-bpf/libbpf-starter-template">https://github.com/eunomia-bpf/libbpf-starter-template</a>基于C语言和 libbpf 框架的eBPF项目模板</li>
<li><a href="https://github.com/eunomia-bpf/cilium-ebpf-starter-template">https://github.com/eunomia-bpf/cilium-ebpf-starter-template</a>基于C语言和cilium/ebpf框架的eBPF项目模板</li>
<li><a href="https://github.com/eunomia-bpf/libbpf-rs-starter-template">https://github.com/eunomia-bpf/libbpf-rs-starter-template</a>基于Rust语言和libbpf-rs框架的eBPF项目模板</li>
<li><a href="https://github.com/eunomia-bpf/eunomia-template">https://github.com/eunomia-bpf/eunomia-template</a>基于C语言和eunomia-bpf框架的eBPF项目模板</li>
</ul>
<p>这些启动模板包含以下功能:</p>
<ul>
<li>一个 Makefile让您可以一键构建项目</li>
<li>一个 Dockerfile用于为您的 eBPF 项目自动创建一个容器化环境并发布到 Github Packages</li>
<li>GitHub Actions用于自动化构建、测试和发布流程</li>
<li>eBPF 开发所需的所有依赖项</li>
</ul>
<blockquote>
<p>通过将现有仓库设置为模板,您和其他人可以快速生成具有相同基础结构的新仓库,从而省去了手动创建和配置的繁琐过程。借助 GitHub 模板仓库,开发者可以专注于项目的核心功能和逻辑,而无需为基础设置和结构浪费时间。更多关于模板仓库的信息,请参阅官方文档:<a href="https://docs.github.com/en/repositories/creating-and-managing-repositories/creating-a-template-repository">https://docs.github.com/en/repositories/creating-and-managing-repositories/creating-a-template-repository</a></p>
</blockquote>
<h2 id="总结"><a class="header" href="#总结">总结</a></h2>
<p>eBPF 程序的开发和使用流程可以概括为如下几个步骤:</p>
<ul>
<li>定义 eBPF 程序的接口和类型:这包括定义 eBPF 程序的接口函数,定义和实现 eBPF 内核映射maps和共享内存perf events以及定义和使用 eBPF 内核帮助函数helpers</li>
<li>编写 eBPF 程序的代码:这包括编写 eBPF 程序的主要逻辑,实现 eBPF 内核映射的读写操作,以及使用 eBPF 内核帮助函数。</li>
<li>编译 eBPF 程序:这包括使用 eBPF 编译器(例如 clang将 eBPF 程序代码编译为 eBPF 字节码,并生成可执行的 eBPF 内核模块。ecc 本质上也是调用 clang 编译器来编译 eBPF 程序。</li>
<li>加载 eBPF 程序到内核:这包括将编译好的 eBPF 内核模块加载到 Linux 内核中,并将 eBPF 程序附加到指定的内核事件上。</li>
<li>使用 eBPF 程序:这包括监测 eBPF 程序的运行情况,并使用 eBPF 内核映射和共享内存进行数据交换和共享。</li>
<li>在实际开发中,还可能需要进行其他的步骤,例如配置编译和加载参数,管理 eBPF 内核模块和内核映射,以及使用其他高级功能等。</li>
</ul>
<p>需要注意的是BPF 程序的执行是在内核空间进行的,因此需要使用特殊的工具和技术来编写、编译和调试 BPF 程序。eunomia-bpf 是一个开源的 BPF 编译器和工具包,它可以帮助开发者快速和简单地编写和运行 BPF 程序。</p>
<p>您还可以访问我们的教程代码仓库 <a href="https://github.com/eunomia-bpf/bpf-developer-tutorial">https://github.com/eunomia-bpf/bpf-developer-tutorial</a> 以获取更多示例和完整的教程,全部内容均已开源。我们会继续分享更多有关 eBPF 开发实践的内容,帮助您更好地理解和掌握 eBPF 技术。</p>
<blockquote>
<p>原文地址:<a href="https://eunomia.dev/zh/tutorials/1-helloworld/">https://eunomia.dev/zh/tutorials/1-helloworld/</a> 转载请注明出处。</p>
</blockquote>
</main>
<nav class="nav-wrapper" aria-label="Page navigation">
<!-- Mobile navigation buttons -->
<a rel="prev" href="../0-introduce/index.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
<i class="fa fa-angle-left"></i>
</a>
<a rel="next prefetch" href="../2-kprobe-unlink/index.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
<i class="fa fa-angle-right"></i>
</a>
<div style="clear: both"></div>
</nav>
</div>
</div>
<nav class="nav-wide-wrapper" aria-label="Page navigation">
<a rel="prev" href="../0-introduce/index.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
<i class="fa fa-angle-left"></i>
</a>
<a rel="next prefetch" href="../2-kprobe-unlink/index.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
<i class="fa fa-angle-right"></i>
</a>
</nav>
</div>
<script>
window.playground_copyable = true;
</script>
<script src="../elasticlunr.min.js"></script>
<script src="../mark.min.js"></script>
<script src="../searcher.js"></script>
<script src="../clipboard.min.js"></script>
<script src="../highlight.js"></script>
<script src="../book.js"></script>
<!-- Custom JS scripts -->
</div>
</body>
</html>