0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

Linux Tracing System以及实例解决eBPF程序中遇到的问题

Linux阅码场 来源:Linux阅码场 作者:Linux阅码场 2022-06-13 11:45 次阅读

一、讲座内容简要描述

本次讲座内容分为两部分

1)Linux Tracing System浅析

当初学者接触到Linux平台的tracing系统时,常常被各种词语弄得晕头转向:比如 Kprobe,Tracepoint,Linux Auditing subsystem(auditd),systemtap,LTTng,perf,trace-cmd,eBPF,bpftrace,BCC等等。初学者往往会有以下疑问:这些专业词语是什么意思?它们之间有什么关系?每种tracing技术的优缺点是什么?应该选择哪种技术?为什么eBPF从中脱颖而出,近年来得到广泛关注?

本次讲座尝试从统一的视角来梳理和对比这些技术的异同点,并尝试回答这些问题。

2)eBPF开发经验分享

eBPF目前正在高速发展,很多坑和解决办法缺乏官方文档。本次讲座主要介绍主讲人在eBPF开发实践中经常遇到的问题,包括开发框架的选择,多内核版本兼容性问题,如何为低版本内核生成BTF文件,eBPF验证机制与编译器优化机制的不一致问题,eBPF在ARM架构遇到的问题等等。

二、Linux Tracing System浅析

对于Linux Tracing System尤其是目前最火的eBPF技术来说,主要是通过探针技术,实现特定事件的追踪和采样,达到增强内核行为可观测性、优化系统性能、动态监测网络和加固系统安全的目的。如下图所示,我将Linux Tracing System细分为三个维度,包括1)数据源(内核态),负责提供数据地来源;2)Tracing框架(内核态),负责对接数据源,采集解析发送数据,并对用户态提供接口;3)以及前端工具/库(用户态),对接Tracing内核框架,直接与用户交互,负责采集配置和数据分析。

下面将从这三个维度自下而上地对Linux Tracing System进行梳理和分析。

7f07b7fe-dd51-11ec-ba43-dac502259ad0.png

1.1.数据源(内核态)介绍

如下图所示,从数据提供方的角度来看,数据源可以分成硬件探针、软件探针(又分为动态探针以及静态探针),也就是获取底层数据源的方式和手段。顾名思义,硬件探针技术就是通过在硬件设备上(比如芯片)插入探针,捕获硬件层次行为;而软件探针技术则是通过软件的方式插入探针,捕获软件层次的行为。这些探针技术负责提供数据,上层的Tracing工具和框架则基于这些探针技术来采集数据,并对数据进一步整理、分析、和展现给用户。

7f3327f4-dd51-11ec-ba43-dac502259ad0.png

硬件探针

HPC: Hardware Performance Counter是CPU硬件提供的一种常见的数据源,如下图所示,它能够监控CPU级别的事件,比如执行的 指令数,跳转指令数,Cache Miss等等,被广泛应用于性能调试(Vtune, Perf)、攻击检测等等

1)HPC事件列表

7fa68f82-dd51-11ec-ba43-dac502259ad0.png

2)HPC数据案例

对于此类硬件数据,我们通常使用用户态工具perf来进行采集,下图展示了一个具体的案例。

7ff3fe2a-dd51-11ec-ba43-dac502259ad0.png

LBR: Last Branch RecordCPU硬件提供的另一种特性,它能够记录每条分支(跳转)指令的源地址和目的地址。基于LBR硬件特性,可实现调用栈信息的记录

在系统性能优化领域以及调试程序时经常使用的性能分析利器:火焰图(Flame Graph)也可以基于LBR的数据生成,使用命令perf record -F 99 -a --call-graph lbr即可得到完整直观的火焰图数据。

802848ce-dd51-11ec-ba43-dac502259ad0.png

火焰图示例

软件探针(静态探针):

静态内核探针指的内核运行之前,在内核源代码或者二进制中插入预先设置好的钩子函数,内核运行时触发生效的探针方案。

Tracepoint:Tracepoint是一种典型的静态探针。它通过在内核源代码中插入预先定义的静态钩子函数来实现内核行为的监控。简单地来看,大家可以把Tracepoint的原理等同于调试程序时加入的printf函数。

下图展示了2012年,内核引入sched_process_execTracepoint时的commit,可以看到,首先用TRACE_EVENT宏定义了新增Tracepoint的名字和参数等信息,然后在内核函数exec_binprm的源代码中加入了钩子函数trace_sched_process_exec。每当程序执行二进制时,都会触发exec_binprm函数,继而触发trace_sched_process_exec钩子函数。Tracing工具和框架将自定义的函数挂载到该钩子函数上,来采集程序执行行为日志。

8061da94-dd51-11ec-ba43-dac502259ad0.png

静态探针的优点:

稳定(内核开发者会负责维护该函数的稳定性)

性能好

静态探针的缺点

需要修改内核代码来添加新的静态探针

内核支持的静态探针数量有限

软件探针(动态探针):

有了静态探针,为什么还需要动态探针呢?主要原因是静态探针都是人工添加的,支持的数量有限,而动态探针就是为了解决这个问题,它能够支持Hook几乎所有的内核函数。

KprobesKprobe是一个典型的动态探针,如下图所示,在内核运行时,Kprobe技术将需要监控的内核函数的指令动态替换,使得该函数的控制流跳转到用户自定义的处理函数上。当内核执行到该监控函数时,相应的用户自定义处理函数被执行,然后继续执行正常的代码路径。

80a00df0-dd51-11ec-ba43-dac502259ad0.png

动态探针的优点:

可以Hook几乎所有的内核函数

动态探针的缺点

不稳定(函数的变更、编译器的优化等都可能导致采集程序的失效)

性能相对较差

软件探针(动/静态探针):

静态探针性能好,但支持的数量有限,动态探针支持的数量多,但不稳定、性能相对较差,那么是否存在一种技术,能同时兼顾静态和动态的优势呢?答案是动静态结合的探针方案。

Function Hooks(Ftrace): Function HooksFtrace引入一种动静态结合的探针方案如下图所示,静态指的是它通过gcc编译器,在内核编译阶段,在内核函数的入口处插入了预留的特定指令,当内核运行时,它会将预留的特定指令替换为跳转指令(callftrace_caller),使得内核函数的控制流跳转到用户自定义函数上,达到数据监控的目的。

80f54bda-dd51-11ec-ba43-dac502259ad0.png

Ftrace和Function Tracer

Function Hooks(Ftrace)的优点

相比于Tracepoint和Kprobe,Function Hooks最显著的功能性特点是它能够方便地监控内核函数的调用关系,如下图所示,监控了内核函数exec_binprm的所有子函数调用关系。

814a3906-dd51-11ec-ba43-dac502259ad0.png

上面分析完各种动态和静态探针的方案和优缺点后,从开发者代码多功能可控的角度出发,建议优先使用静态探针方案。

1.2.Linux Tracing System 发展历程

2004年4月,Linux Auditing subsystem(auditd)被引入内核2.6.6-rc1

2005年4月,Kprobe被引入内核2.6.11.7

2006年,LTTng发布(至今没有合入内核)

2008年10月 ,Kernel Tracepoint 被引入内核(v2.6.28)。

2008年,Ftrace被引入内核(包括compile time function hooks)。

2009年,perf被引入内核

2009年,SystemTap发布(至今没有合入内核)

2014年Alexei Starovoitov将eBPF引入内核

1.3.Linux Tracing 框架方案对比

818c89c8-dd51-11ec-ba43-dac502259ad0.png

eBPF的优势对比:

稳定:通过验证器,防止用户编写的程序导致内核崩溃

免安装:eBPF内置于linux内核,无需安装额外以来

内核编程:支持开发者插入自定义的代码逻辑(包括数据采集、分析和过滤)到内核中运行

2.eBPF框架开发分析

2.1 eBPF基础架构

eBPF程序分为两部分: 用户态和内核态代码。

eBPF内核代码:

这个代码首先需要经过编译器(比如LLVM)编译成eBPF字节码,然后字节码会被加载到内核执行。所以 这部分代码理论上用什么语言编写都可以,只要编译器支持将该语言编译为eBPF字节码即可。

目前绝大多数工具都是用的C语言来编写eBPF内核代码,包括BCC。

bpftrace提供了一种易用的脚本语言来帮助用户快速高效的使用eBPF功能,其背后的原理还是利用LLVM 将脚本转为eBPF字节码。

eBPF用户态代码:

这部分代码负责将eBPF内核程序加载到内核,与eBPF MAP交互,以及接收eBPF内核程序发送出来的数据。这个功能的本质上是通过Linux OS提供的syscall(bpf syscall + perf_event_open syscall)完成的,因此这 部分代码你可以用任何语言实现。比如BCC使用python,libbpf使用c或者c++,TRACEE使用Go等等。

81bbd886-dd51-11ec-ba43-dac502259ad0.png

2.2 eBPF数据源

性能分析大师Brendan Gregg(Intel Fellow)总结的Linux BPF Tracing Tools上展示了丰富多彩的eBPF钩子类型,这些钩子类型提供了可以加载BPF程序的范围。

fentry/fexit

Tracepoints

network devices (tc/xdp)

network routes

TCP congestion algorithms

sockets (data level)

kernel functions (kprobes)

userspace functions (uprobes)

system calls

82198580-dd51-11ec-ba43-dac502259ad0.png

2.3 eBPF框架的发展历程

2014年9月 引入了bpf() syscall,将eBPF引入用户态空间。

自带迷你libbpf库,简单对bpf()进行了封装,功能是将eBPF字节码加载到内核。

2015年2月份 Kernel 3.19 引入bpf_load.c/h文件,对上述迷你libbpf库再进行封装,功能是将eBPF elf二进制文件加载到内核(目前已过时,不建议使用)。

2015年4月 BCC项目创建,提供了eBPF一站式编程

1.创建之初,基于上述迷你libbpf库来加载eBPF字节码。

2. 提供了Python接口。

2015年11月 Kernel 4.3 引入标准库 libbpf

1. 该标准库由Huawei 2012 OS内核实验室的王楠提交。

2018年 为解决BCC的缺陷,CO-RE(Compile Once, Run Everywhere)的想法被提出并实现,最后达成共识:libbpf + BTF + CO-RE代表了eBPF的未来,BCC底层实现逐步转向libbpf。

2.4 eBPF可移植性痛点和解决方案

技术痛点

在内核版本A上编译的eBPF程序,无法直接在另外一个内核版本B上运行。造成可以执行差的根本原因在于eBPF程序访问的内核数据结构(内存空间)是不稳定的,经常随内核版本更迭而变化。

目前使用BCC的方案通过在部署机器上动态编译eBPF源代码可以来解决移植性问题。每一次eBPF程序运行都需要进行一次编译,而且需要在部署机器上按照上百兆大小的依赖,如编译器和头文件Clang/LLVM + Linux headers等。同时在Clang/LLVM编译过程中需要消耗大量的资源(CPU/内存),对业务性能也会造成很大影响。

解决方案(CO-RE Compile Once,Run Everywhere):

1)BTF:将内核数据结构信息高效压缩和存储(相比于DWARF,可达到超过100倍的 压缩比)

2)LLVM/Clang编译器:编译eBPF代码的时候记录下relocation相关的信息

3)Libbpf:基于BTF和编译器提供的信息,动态relocate数据结构

其中BTF为重要组成部分,Linux Kernel 5.2及以上版本自带BTF文件,低版本需要手动移植。

通过分析内核源码,可以发现BTF文件的生成并不需要改动内核,只依赖:

带有debug info的vmlinux image

pahole

LLVM

这意味着,我们可以自己为低版本内核生产BTF文件,以此让低内核版本支持CORE。

低版本内核BTF文件

准备工作

·安装pahole软件(1.16+)

·https://git.kernel.org/pub/scm/devel/pahole/pahole.git

·安装LLVM(11+)

·获取目标低版本内核的vmlinux文件(带有debug info),文件保存在{vmlinux_file_path}

·通过源下载

·比如对于CentOS,通过yum install kernel-debuginfo可以下载vmlinux

·源码编译内核,获取vmlinux

生成BTF:

·利用pahole在vmlinux文件中生成BTF信息,执行以下命令:

·pahole -J {vmlinux_file_path}

·将BTF信息单独输出到新文件{BTF_file_path},执行以下命令:

·llvm-objcopy --only-section=.BTF --set-section-flags .BTF=alloc,readonly --strip- all {vmlinux_file_path} {BTF_file_path}

·去除非必要的符号信息,降低BTF文件的大小,得到最终的BTF文件(大小约2~3MB):

·strip -x {BTF_file_path}

2.5 eBPF程序实例分析(一个Print引发的惨案)

eBPF程序会被LLVM编译为eBPF字节码,eBPF字节码需要通过eBPF Verifier的(静态)验证后,才能真正运行。边界检查是eBPF Verifier的重点工作,目的是为了防止eBPF程序内存越界访问。接下来通过在eBPF程序中简单的增加、删减print打印信息触发不同原因的几种边界检查异常导致验证失败的例子,进一步讲解深层的原理。

程序实验环境:

1)LLVM 11

2)Linux Kernel 5.8

3)Libbpf commit @9c44c8a

边界检查案例:

1)内存越界:

SEC("kprobe/do_unlinkat")int BPF_KPROBE(do_unlinkat, int dfd, struct filename *name){  // 获取一个数组指针array(数组MAX_SIZE为16个字节)  u32 key = 0;  char *array = bpf_map_lookup_elem(&array_map, &key);  if (array == NULL)    return 0;  // 获取当前运行程序的CPU编号(当前机器的CPU有16个核)  unsigned int pos = bpf_get_smp_processor_id();       // 根据下表修改数组的值      array[pos] = 1;      return 0;}

上述代码编译运行后,提示Verifier失败,然后使用objdump命令来看一下具体的字节码,通过以下字节码程序,可以看到Verifier失败的原因在于第14行R6寄存器(变量pos)没有进行边界检查导致。

Root Cause:

当eBPF Verifier走到第14行的时候尝试去访问array数组,但是此时数组的下标pos是来自bpf_get_smp_processor_id获取到的unsigned int 类型的动态变量,此时Verifier无法判断变量的具体数值,所以会保守认为可能会达到最大值,这样的话就会超出array数组的范围,造成内存越界。

0000000000000000 :;intBPF_KPROBE(do_unlinkat,intdfd,structfilename*name)0:r1=0;  u32 key = 0;1:  *(u32 *)(r10 - 4) = r12:  r2 = r103:  r2 += -4;      char *array = bpf_map_lookup_elem(&array_map, &key); 4:r1 = 0 ll6:  call 17:  r6 = r0;  if (array == NULL)8:  if r6 == 0 goto +6 ;      unsigned int pos = bpf_get_smp_processor_id();; 9:call 8;      array[pos] = 1; 10:r0 <<= 3211:  r0 >>= 3212:  r6 += r013:  r1 = 1;    array[pos] = 1;14:  *(u8 *)(r6 + 0) = r1

解决方案:

添加边界检查代码

if (pos < MAX_SIZE)   if r0 >15goto+3

2)Verifier验证机制和编译器优化机制不一致导致边界检查不通过

①使用错误寄存器做边界检查:

SEC("kprobe/do_unlinkat")
int BPF_KPROBE(do_unlinkat, int dfd, struct filename *name){  // 获取一个数组指针array(数组MAX_SIZE为16个字节)  u32 key = 0;  char *array = bpf_map_lookup_elem(&array_map, &key); if (array == NULL)    return 0;  // 获取当前运行程序的CPU编号(当前机器的CPU有16个核)  unsigned int pos = bpf_get_smp_processor_id();;  // 修改数值  if (pos < MAX_SIZE){    array[pos] = 1;    pos += 1;  }  // debug代码,输出一些上下文信息  bpf_printk("debug %d %d %d
", bpf_get_current_pid_tgid() >> 32, bpf_get_current_pid_tgid(), array[1]);  // 修改数值  if (pos < MAX_SIZE)    array[pos] = 1;  return 0;}

编译这个代码后Verifier验证通过,可以正常运行。但是此时如果把bpf_printk打印信息删掉,竟然提示Verifier验证失败,原因是R0寄存器(变量pos)没有通过边界检查,但是明明已经加了边界检查代码,怎么还会出现问题,这么神奇!

Root Cause:

8275b88c-dd51-11ec-ba43-dac502259ad0.png

由于编译器的优化策略,导致删减bpf_printk后编译生成的eBPF字节码使用寄存器r1(表示pos变量)来进行边界检查,但是却用r0+1(同样表示pos变量)来访问数组array。

相比之下,从eBPF verifier的角度来看,由于在编译过程中,r1和r0+1的关联性丢失了,导致eBPF verifier无法知道pos变量已经通过了检查,因此错误的认为pos变量没有进行边界检查,不允许程序运行。

②寄存器溢出或重新加载后,状态丢失:

SEC("kprobe/do_unlinkat")
int BPF_KPROBE(do_unlinkat, int dfd, struct filename *name){  //获取一个数组指针array(数组MAX_SIZE为16个字节)  u32 key = 0;  char *array = bpf_map_lookup_elem(&array_map, &key); if (array == NULL)    return 0;  // 获取当前运行程序的CPU编号(当前机器的CPU有16个核)  unsigned long pos = bpf_get_smp_processor_id();;  // 修改数值  if (pos < MAX_SIZE){    for (unsigned long i = 0; i < MAX_SIZE; i++)      bpf_printk("debug %d %d %d
", bpf_get_current_pid_tgid() >> 32,          bpf_get_current_pid_tgid(), array[i]);    array[pos] = 1;  }  return 0;}

在上述边界检查代码中添加一段print调试打印信息后编译验证又会出现Verifier失败,通过排查发现不是已知的两类问题,依然使用objdump查看添加后的字节码信息。

Root Cause:

82bcd4ba-dd51-11ec-ba43-dac502259ad0.png

加入bpf_printk后通过字节码可以看到,代码先使用R0(表示pos变量)进行边界检查。由于当前寄存器数量不足,编译器决定将将R0临时保存到栈上的空间(R10-16,在eBPF字节码中,R10存储存放着 eBPF 栈空间的栈帧指针的地址),这样R0就可以空闲出来,留给其他代码使用,我们称这种行为为寄存器溢出(register spill)。当真正需要使用pos变量的时候,编译器会从栈上(R10-16)将之前保存的内容取出来赋给R1(也表示pos变量),然后使用R1对数组array进行访问。但神奇的是,当寄存器溢出发生时,pos变量的状态丢失了,eBPF忘记了该变量曾经进行了边界检查,导致程序无法通过验证。

解决方案:

在源码中加入 &= 操作符,引导编译器生成理想的eBPF字节码

array[pos &= MAX_SIZE - 1] = 1;

如果上述方法失效,无法引导编译器,那么针对出错的部分源代码人工编写eBPF字节码,替代编译器生成的字节码

#defineSTR(s)#s#defineXSTR(s)STR(s)#defineasm_variable_bound_check(variable)({  asmvolatile(    "%[tmp]&="XSTR(MAX_SIZE-1)"
"    :[tmp]"+&r"(variable)  );})asm_check(pos);array[pos] = 1;

3.总结

本文总结了从动静态探针的角度梳理分析Linux Tracing System以及实例解决eBPF程序中遇到的问题。eBPF目前正在高速发展,很多坑和解决办法缺乏官方文档。本文在以下几点上做了自己的分析和分享,希望对大家更清晰的认识Linux Tracing System和eBPF有所帮助。

1.自下而上的方式分析动静态探针

2.各种场景下动静态探针的选择

3.BPF开发框架的选择

4.多内核版本兼容性问题

5.如何为低版本内核生成BTF文件

6.eBPF边界检查问题分析

7.eBPF Verifier验证机制与编译器优化机制不一致问题

原文标题:Linux Tracing System浅析和eBPF开发经验分享

文章出处:【微信公众号:Linux阅码场】欢迎添加关注!文章转载请注明出处。

审核编辑:彭静
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • cpu
    cpu
    +关注

    关注

    68

    文章

    10437

    浏览量

    206527
  • 接口
    +关注

    关注

    33

    文章

    7635

    浏览量

    148452
  • Linux
    +关注

    关注

    87

    文章

    10988

    浏览量

    206724
  • 硬件
    +关注

    关注

    11

    文章

    2922

    浏览量

    64749

原文标题:Linux Tracing System浅析和eBPF开发经验分享

文章出处:【微信号:LinuxDev,微信公众号:Linux阅码场】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    实战eBPF kprobe函数插桩

    本文作者为团队小伙伴阿松,在Linux文件监控领域实战经验丰富。本次引入eBPF在文件监控上应用,提升文件变更的关联进程信息等。在实现过程中,分享了eBPF kbproe时,被插桩函数超多参数获取的解决方案。
    发表于 11-29 09:03 1526次阅读

    Application Level Tracing概述

    Application Level Tracing概述API介绍头文件函数原型原文地址概述是esp32提供的一种调试方案,通过JTAG接口可以在程序执行时以很小的开销在主机和esp32间传输数据
    发表于 01-07 07:20

    关于 eBPF 安全可观测性,你需要知道的那些事儿

    核来说其存在感极低但观测能力却异常强大(药效好,副作用小):程序沙箱化:通过 eBPF 验证器保护内核稳定运行。侵入性低:无须修改内核代码,且无须停止程序运行。透明化:从内核透明搜集
    发表于 09-08 15:31

    openEuler 倡议建立 eBPF 软件发布标准

    生产环境大规模应用。缺点:应用技术门槛高,且不具备可移植性(比如高内核版本的 eBPF 程序无法移植至低内核版本)。开发态、运行态融合(典型代表 BCC)优点:源码形式发布天然具备可移植性;封装抽象
    发表于 12-23 16:21

    qq版原理linux实例程序代码

    qq版原理linux实例程序代码(对应qq版原理图):
    发表于 05-27 08:48 0次下载
    qq版原理<b class='flag-5'>linux</b>下<b class='flag-5'>实例</b>源<b class='flag-5'>程序</b>代码

    The Linux Programming Interface - A Linux and UNIX System Programming Handbook

    The Linux Programming Interface - A Linux and UNIX System
    发表于 03-02 11:42 7次下载

    System Generator的设计实例

    Xilinx FPGA工程例子源码:System Generator的设计实例
    发表于 06-07 14:41 22次下载

    第9章 Linux驱动程序设计

    9.1 Linux 设备驱动程序 9.2 Linux经典Hello world驱动程序 9.3 Linux字符设备驱动
    发表于 04-11 14:56 3次下载

    eBPF是什么以及eBPF能干什么

    一、eBPF是什么 eBPF是extended BPF的缩写,而BPF是Berkeley Packet Filter的缩写。对linux网络比较熟悉的伙伴对BPF应该比较了解,它通过特定的语法
    的头像 发表于 07-05 15:17 9729次阅读
    <b class='flag-5'>eBPF</b>是什么<b class='flag-5'>以及</b><b class='flag-5'>eBPF</b>能干什么

    eBPF深入理解和实现原理

    简单来说,wBPF 是一个在硬件上直接执行 eBPF 程序的系统。
    的头像 发表于 06-14 09:25 2884次阅读

    Linux 内核:eBPF优势和eBPF潜力总结

    Express Data Path (XDP):网络驱动程序是最早可以附加 XDP BPF 钩子的点。当收到一个数据包时,eBPF 程序就会被触发运行。
    发表于 01-10 11:37 1957次阅读

    eBPF的前世今生?eBPF在使用中遇到的问题有哪些?

    在介绍eBPF (Extended Berkeley Packet Filter)之前,我们先来了解一下它的前身-BPF (Berkeley Packet Filter)伯克利数据包过滤器。
    的头像 发表于 08-12 15:10 1136次阅读
    <b class='flag-5'>eBPF</b>的前世今生?<b class='flag-5'>eBPF</b>在使用中<b class='flag-5'>遇到</b>的问题有哪些?

    基于ebpf的性能工具-bpftrace

    运行情况对于诊断问题、优化性能以及进行安全监控至关重要。bpftrace作为一款强大的跟踪工具,为开发人员和系统管理员提供了一种独特的方式来监视和分析Linux系统的内部运行。本文描述bpftrace的原理和使用。 bpftrace 「bpftrace是基于
    的头像 发表于 09-04 16:02 337次阅读
    基于<b class='flag-5'>ebpf</b>的性能工具-bpftrace

    如何使用Tokio 和 Tracing模块构建异步的网络应用程序

    在 Rust 语言中,Tokio 是一个非常流行的异步运行时,它提供了高效的异步 I/O 操作和任务调度。而 Tracing 则是一个用于应用程序跟踪的框架,它可以帮助我们理解应用程序的行为和性能
    的头像 发表于 09-19 15:29 340次阅读

    ebpf的快速开发工具--libbpf-bootstrap

    )和libbpf的程序eBPF是一种可以在Linux内核中运行的程序,提供了强大的网络过滤、系统调用监控和性能分析等功能。libbpf是一个库,用于加载和管理
    的头像 发表于 09-25 09:04 376次阅读
    <b class='flag-5'>ebpf</b>的快速开发工具--libbpf-bootstrap