BCC小白写测试上下文切换延时程序

一口Linux 2023-03-28 11:50


作者介绍:

张子恒,西安邮电大学研一在读,导师陈莉君老师,刚刚踏入Linux内核学习的小白一枚。



下文Linux内核版本5.4

0



启发



1

学习兴趣起源于论文《Linux上下文切换性能测试的一种新方法》的阅读,该论文提出了一种在用户态编写应用程序并且调用schedu_yield()系统调用主动放弃处理器实现任务切换的测试方法,然后与传统的使用管道读写切换、在内核态测试context_switch()函数的开销等方法进行了对比分析(传统方法分析见下图),最后结果表明,使用该方法测试上下文切换的准确性和便捷性均有所提高。

论文中提出的新方法有个明显的缺陷:原理上获得的上下文切换延时还包含了系统调用等其他指令的开销 overhead,但是他这个用户态的程序无法测量也无法避免这个overhead。

结合所学知识,受此启发:直接对内核函数context_switch()进行测试才是最准确最可靠的方法,因此可以写一个ebpf程序直接对context_switch()函数进行测试,这样的ebpf程序不仅测试出来的结果更加准确,而且操作起来也很方便,于是就有了下面的学习历程。

0



思路



2

首先是整理思路,作为eBPF小白的我一开始也是一筹莫展,于是翻看起了《BPF之巅  洞悉Linux系统和应用性能》这本书,书中有这么几个地方很受启发:

  • P57:

由于:

内核中没有context_switch的静态跟踪点,所以这里选择用kprobes动态跟踪。(这里忘记对kprobe进行查询,导致下面绕了一波远路,这也提醒学者在写eBPF程序之前一定要先找探针)

  • P49:

kprobes 可以对任何内核函数进行插桩,它还可以对函数内部的指令进行插桩。它可以实时在生产环境系统中启用,不需要重启系统,也不需要以特殊方式重启内核。这是一项令人惊叹的能力,这意味着我们可以对 Linux 中数以万计的内核函数任意插桩根据需要生成指标。

kprobes 技术还有另外一个接口,即 kretprobes,用来对内核函数返回时进行插桩以获取返回值。当用 kprobes 和 kretprobes 对同一个函数进行插时,可以使用时间戳来记录函数执行的时长。这在性能分析中是一个重要的指标。

  • P52:

BCC:attach_kprobe 和 attach_kretprobe

于是我就准备从kprobes和kretprobes这两种探针入手,对context_switch函数的执行时长进行测量。

0



实践



3

最后就是实践了,我利用不同的映射输出函数写了三个版本,每个版本都是上一个版本的优化。

(一)起初代码

主要涉及函数:

  • bpf_trace_printk()

    语法: int bpf_trace_printk(const char *fmt, ...)

    Return: 0 on success

    printf()到公共trace_pipe (/sys/kernel/debug/tracing/trace_pipe)的一个简单的内核工具。对于一些快速示例,这是可以的,但有限制:最多3个args,只有1% s,并且trace_pipe是全局共享的。

  • trace_print()

    语法: BPF.trace_print(fmt="fields")

    该方法持续读取全局共享的/sys/kernel/debug/tracing/trace_pipe文件并打印其内容。可以通过BPF和bpf_trace_printk()函数写入该文件。

    fmt: 可选,并且可以包含字段格式字符串。默认为None

cs.c 代码如下:

#include 

BPF_HASH(start, u32);

int do_entry(struct pt_regs *ctx)        //pt_regs结构定义了在系统调用或其他内核条目期间将寄存器存储在内核堆栈上的方式
{
        u32 pid;
        u64 ts;

        pid = bpf_get_current_pid_tgid();
        ts = bpf_ktime_get_ns();         //bpf_ktime_get_ns返回自系统启动以来所经过的时间(以纳秒为单位)。不包括系统挂起的时间。
        start.update(&pid,&ts);
        return 0;
}

int do_return(struct pt_regs *ctx)
{
        u32 pid;
        u64 *tsp, delta;

        pid = bpf_get_current_pid_tgid();
        tsp = start.lookup(&pid);

        if (tsp != 0) {
                delta = bpf_ktime_get_ns() - *tsp;         //获得context_switch函数执行时间
                bpf_trace_printk("The time of context_switch is %lu.\n",delta/1000);
                start.delete(&pid);
}

        return 0;
}

cs.py 代码如下:

from __future__ import print_function
from bcc import BPF

# load BPF program
b = BPF(src_file = "cs.c")
b.attach_kprobe(event="context_switch", fn_name="do_entry")
b.attach_kretprobe(event="context_switch", fn_name="do_return")

b.trace_print()

运行结果,遇到如下问题:

一开始以为是因为context_switch函数没有被导出的原因,经师兄提醒,查看了下探针:

发现没有context_switch的kprobe,相关函数学习:

nr_context_switches             //统计目前所有处理器总共的context switch次数
paravirt_end_context_switch         //和半虚拟化相关
paravirt_start_context_switch
rcu_note_context_switch         //更新全局状态为RCU,标识当前CPU发生上下文的切换
xen_end_context_switch         //和虚拟话技术相关

这些函数和上下文切换时间的计算都关联不上。

教训:在编写BCC程序时,一定要先用bpftrace查看一下该插桩点是否存在!

(二)纠正代码

退一步,如果无法对context_switch函数进行跟踪,那么对内核函数schedule进行跟踪就是最好地选择(不存在__schedule函数的探针),虽然存在一定的误差,但是相比于通过用户态程序测量更加准确,相比于在内核里插桩测试更加简便,经查询,schedule函数只存在动态插桩点位:

schedule() 源码:

asmlinkage __visible void __sched schedule(void)
{
        struct task_struct *tsk = current;

        sched_submit_work(tsk);         //sched_submit_work用于检测当前进程是否有plugged io需要处理,由于当前进程执行schedule后,有可能会进入休眠,所以在休眠之前需要把plugged io处理掉,防止死锁。

        do {

                preempt_disable();         //禁止抢占

                __schedule(false);         //->context_switch false为禁用抢占标志
                sched_preempt_enable_no_resched();         //开启内核抢占
        } while (need_resched());         //检查当前进程是否设置了重调度标志
        sched_update_worker(tsk);     //更新worker的信息,告诉工作队列我又回来了
}

对于数据的输出,由于bpf_trace_printk()是把数据写到公共trace_pipe,可能与其他程序和跟踪器冲突,因此选用BPF_PERF_OUTPUT()进行数据的输出,它的工作原理是创建一个BPF表,用于通过缓冲区向用户空间推出自定义事件数据。这是将每个事件数据推入用户空间的首选方法。

主要涉及函数:

  • BPF_PERF_OUTPUT

    语法: BPF_PERF_OUTPUT(name)

    创建一个BPF表,用于通过缓冲区向用户空间推出自定义事件数据。这是将每个事件数据推入用户空间的首选方法。

  • perf_submit()

    语法: int perf_submit((void *)ctx, (void *)data, u32 data_size)

    Return: 0 on success

    BPF_PERF_OUTPUT表的方法,用于向用户空间提交自定义事件数据。

    ctx参数在kprobes或kretprobes中提供。对于SCHED_CLSSOCKET_FILTER程序,必须改用struct __sk_buff *skb

  • open_perf_buffer()

    语法: table.open_perf_buffers(callback, page_cnt=N, lost_cb=None)

    它对BPF中定义为BPF_PERF_OUTPUT()的表进行操作,并关联回调Python函数callback,以便在性能环缓冲区中有可用数据时调用。这是将每个事件数据从内核传输到用户空间的推荐机制的一部分。缓冲区的大小可以通过page_cnt参数指定,该参数必须是两页数的幂,默认为8。如果回调处理数据的速度不够快,一些提交的数据可能会丢失。lost_cb将被调用来记录/监视丢失的计数。如果lost_cb 是默认的None值,它将只打印一行消息到stderr

  • perf_buffer_poll()

    语法: BPF.perf_buffer_poll(timeout=T)

    它从所有打开的性能环缓冲区轮询,调用为每个条目调用open_perf_buffer时提供的回调函数。

    timeout参数是可选的,以毫秒为单位。如果没有它,轮询将无限期地继续进行。

cs.c 代码如下:

#include 

struct data_t{
        u64 t1;
        u64 t2;
        u64 delay;
};

BPF_PERF_OUTPUT(events);
BPF_HASH(start, u32);

int do_entry(struct pt_regs *ctx)        //pt_regs结构定义了在系统调用或其他内核条目期间将寄存器存储在内核堆栈上的方式
{
        u32 pid;
        u64 ts;

        pid = bpf_get_current_pid_tgid();
        ts = bpf_ktime_get_ns()/1000;         //bpf_ktime_get_ns返回自系统启动以来所经过的时间(以纳秒为单位)。不包括系统挂起的时间。
        start.update(&pid,&ts);

        return 0;
}

int do_return(struct pt_regs *ctx)
{
        struct data_t data={};
        data.t2= bpf_ktime_get_ns()/1000;
        u32 pid;
        u64 *tsp, delta;

        pid = bpf_get_current_pid_tgid();
        tsp = start.lookup(&pid);

        if (tsp != 0) {

                data.t1 = *tsp;

                data.delay = data.t2 - data.t1;
                start.delete(&pid);
                events.perf_submit(ctx,&data,sizeof(data));
        }

                return 0;
}

cs.py 代码如下:

from __future__ import print_function
from bcc import BPF
import time

# load BPF program
b = BPF(src_file = "cs.c")
b.attach_kprobe(event="schedule", fn_name="do_entry")
b.attach_kretprobe(event="schedule", fn_name="do_return")

print("Tracing for Data's... Ctrl-C to end")

#声明了一个名为print_event()的回调函数,用于处理来自perf缓冲区的一个事件
def print_event(cpu, data, size):
        event = b["events"].event(data)
        print("t1:%d t2:%d delay:%d" % (event.t1,event.t2,event.delay))

#将回调函数注册到名为events的perf事件缓冲区
b["events"].open_perf_buffer(print_event)

while 1:

        try:

                b.perf_buffer_poll()        #轮询打开perf缓冲区。如果有事件,回调函数会执行
                time.sleep(1)
        except KeyboardInterrupt:        #Python的except用来捕获所有异常, 因为Python里面的每次错误都会抛出 一个异常,所以每个程序的错误都被当作一个运行时错误。捕获指定异常except <异常名>
                exit()

cs.py 代码中time.sleep(1)说明:由于上下文切换延时特别小,因此schedule函数的触发非常频繁,每次b.perf_buffer_poll()的输出数据都特别多,会导致ctrl+c很难抢占到CPU对这个程序关闭,所以得加入time.sleep(1)以使得能有时间间隙去关闭该程序

避坑:加载到内核中的代码(这里即cs.c )不要用全局变量,用map相关函数

运行结果:

弊端:虽然可以正常运行,但是事件发生地很频繁,导致输出过快、输出量过大,不能直观的了解到数据的情况。

(三)优化代码

可以对事件进行汇总后再输出,打印为以2为幂的直方图。

主要涉及函数:

  • BPF_HISTOGRAM

    语法: BPF_HISTOGRAM(name [, key_type [, size ]])

    创建一个名为name的直方图映射,带有可选参数。

    默认: BPF_HISTOGRAM(name, key_type=int, size=64)

  • map.increment()

    语法: map.increment(key[, increment_amount])

    按 increment_amount增加键的值,默认为1。用于直方图。

  • bpf_log2l()

    语法: unsigned int bpf_log2l(unsigned long v)

    返回所提供值的log-2。这通常用于为直方图创建索引,以构造2次方的直方图。

  • print_log2_hist()

    语法: table.print_log2_hist(val_type="value", section_header="Bucket ptr", section_print_fn=None)

    将表打印为ASCII中的log2直方图。表必须存储为log2,这可以使用BPF函数 bpf_log2l()来实现。

    参数:val_type: 可选,列标题;section_header: 如果直方图有一个辅助键,那么将打印多个表,section_header可以用作每个表的头描述;section_print_fn: 如果section_print_fn不是None,它将被传递桶值。

cs.c 代码如下:

#include 

BPF_HASH(start, u32);
BPF_HISTOGRAM(dist);

int do_entry(struct pt_regs *ctx)        //pt_regs结构定义了在系统调用或其他内核条目期间将寄存器存储在内核堆栈上的方式
{
        u64 t1= bpf_ktime_get_ns()/1000;         //bpf_ktime_get_ns返回自系统启动以来所经过的时间(以纳秒为单位)。不包括系统挂起的时间。;
        u32 pid = bpf_get_current_pid_tgid();
        start.update(&pid,&t1);

        return 0;
}

int do_return(struct pt_regs *ctx)
{
        u64 t2= bpf_ktime_get_ns()/1000;
        u32 pid;
        u64 *tsp, delay;

        pid = bpf_get_current_pid_tgid();
        tsp = start.lookup(&pid);

        if (tsp != 0)
        {
                delay = t2 - *tsp;
                start.delete(&pid);
                dist.increment(bpf_log2l(delay));
        }

        return 0;
}

cs.py 代码如下:

from __future__ import print_function
from bcc import BPF
from time import sleep

# load BPF program
b = BPF(src_file = "cs.c")
b.attach_kprobe(event="schedule", fn_name="do_entry")
b.attach_kretprobe(event="schedule", fn_name="do_return")

print("Tracing for Data's... Ctrl-C to end")

#trace until Ctrl-C
try:
        sleep(99999999)
except KeyboardInterrupt:
        print()

#output
b["dist"].print_log2_hist("cs delay")

运行结果:

疑惑:这个运行结果和上面的那个运行结果差别比较大,这个运行结果的的值总体上偏大,而上面那个运行结果的值更符合常理,这个运行结果不应该是对上面那个运行结果的统计输出吗?为什么差别这么大呢?

解决:考虑到可能是不同进程环境导致的,于是我同时运行这两个测试程序,以保证进程环境的相同,得到以下运行结果:

经过数据对比,在数值上,这些数据是对的上的,说明优化后的测试程序就是对优化前的测试程序数据的统计输出,之前单独运行这两个测试程序的运行结果不同就是因为不同进程环境导致的。

0



参考资料



4

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

  2. 《BPF之巅  洞悉Linux系统和应用性能》

end


一口Linux 


关注,回复【1024】海量Linux资料赠送

精彩文章合集


文章推荐

【专辑】ARM
【专辑】粉丝问答
专辑linux入门
专辑计算机网络
专辑Linux驱动
【干货】嵌入式驱动工程师学习路线
【干货】Linux嵌入式所有知识点-思维导图

一口Linux 写点代码,写点人生!
评论 (0)
  •     在研究Corona现象时发现:临界电压与介电材料表面的清洁程度有关。表面越清洁的介电材料,临界电压越高;表面污染物越多的地方,越容易“爬电”。关于Corona现象,另见基础理论第007篇。    这里说的“污染物”,定义为——可能影响介电强度或表面电阻率的固体、液体或气体(电离气体)的任何情况。    IEC 60664-1 (对应GB/T 16935.1-2023) 定义了 Pollution Degree,中文术语是“污染等
    电子知识打边炉 2025-04-07 22:06 98浏览
  • 在人工智能技术飞速发展的今天,语音交互正以颠覆性的方式重塑我们的生活体验。WTK6900系列语音识别芯片凭借其离线高性能、抗噪远场识别、毫秒级响应的核心优势,为智能家居领域注入全新活力。以智能风扇为起点,我们开启一场“解放双手”的科技革命,让每一缕凉风都随“声”而至。一、核心技术:精准识别,无惧环境挑战自适应降噪,听懂你的每一句话WTK6900系列芯片搭载前沿信号处理技术,通过自适应降噪算法,可智能过滤环境噪声干扰。无论是家中电视声、户外虫鸣声,还是厨房烹饪的嘈杂声,芯片均能精准提取有效指令,识
    广州唯创电子 2025-04-08 08:40 176浏览
  •   工业自动化领域电磁兼容与接地系统深度剖析   一、电磁兼容(EMC)基础认知   定义及关键意义   电磁兼容性(EMC),指的是设备或者系统在既定的电磁环境里,不但能按预期功能正常运转,而且不会对周边其他设备或系统造成难以承受的电磁干扰。在工业自动化不断发展的当下,大功率电机、变频器等设备被大量应用,现场总线、工业网络等技术也日益普及,致使工业自动化系统所处的电磁环境变得愈发复杂,电磁兼容(EMC)问题也越发严峻。   ​电磁兼容三大核心要素   屏蔽:屏蔽旨在切断电磁波的传播路
    北京华盛恒辉软件开发 2025-04-07 22:55 218浏览
  • HDMI从2.1版本开始采用FRL传输模式,和2.0及之前的版本不同。两者在物理层信号上有所区别,这就需要在一些2.1版本的电路设计上增加匹配电路,使得2.1版本的电路能够向下兼容2.0及之前版本。2.1版本的信号特性下面截取自2.1版本规范定义,可以看到2.1版本支持直流耦合和交流耦合,其共模电压和AVCC相关,信号摆幅在400mV-1200mV2.0及之前版本的信号特性HDMI2.0及之前版本采用TMDS信号物理层,其结构和参数如下:兼容设计根据以上规范定义,可以看出TMDS信号的共模电压范
    durid 2025-04-08 19:01 138浏览
  •   物质扩散与污染物监测系统软件:多领域环境守护的智能中枢   北京华盛恒辉物质扩散与污染物监测系统软件,作为一款融合了物质扩散模拟、污染物监测、数据分析以及可视化等多元功能的综合性工具,致力于为环境科学、公共安全、工业生产等诸多领域给予强有力的技术支撑。接下来,将从功能特性、应用场景、技术实现途径、未来发展趋势等多个维度对这类软件展开详尽介绍。   应用案例   目前,已有多个物质扩散与污染物监测系统在实际应用中取得了显著成效。例如,北京华盛恒辉和北京五木恒润物质扩散与污染物监测系统。这
    华盛恒辉l58ll334744 2025-04-09 14:54 29浏览
  • 文/Leon编辑/侯煜‍就在小米SU7因高速交通事故、智驾性能受到质疑的时候,另一家中国领先的智驾解决方案供应商华为,低调地进行了一场重大人事变动。(详情见:雷军熬过黑夜,寄望小米SU7成为及时雨)4月4日上午,有网友发现余承东的职务发生了变化,华为官网、其个人微博认证信息为“常务董事,终端BG董事长”,不再包括“智能汽车解决方案BU董事长”。余承东的确不再兼任华为车BU董事长,但并非完全脱离华为的汽车业务,而是聚焦鸿蒙智行。据悉,华为方面寻求将车BU独立出去,但鸿蒙智行仍留在华为终端BG部门。
    华尔街科技眼 2025-04-09 15:28 26浏览
  •   卫星图像智能测绘系统:地理空间数据处理的创新引擎   卫星图像智能测绘系统作为融合卫星遥感、地理信息系统(GIS)、人工智能(AI)以及大数据分析等前沿技术的综合性平台,致力于达成高精度、高效率的地理空间数据采集、处理与应用目标。借助自动化、智能化的技术路径,该系统为国土资源管理、城市规划、灾害监测、环境保护等诸多领域输送关键数据支撑。   应用案例   目前,已有多个卫星图像智能测绘系统在实际应用中取得了显著成效。例如,北京华盛恒辉北京五木恒润卫星图像智能测绘系统。这些成功案例为卫星
    华盛恒辉l58ll334744 2025-04-08 16:19 69浏览
  • 在万物互联时代,智能化安防需求持续升级,传统报警系统已难以满足实时性、可靠性与安全性并重的要求。WT2003H-16S低功耗语音芯片方案,以4G实时音频传输、超低功耗设计、端云加密交互为核心,重新定义智能报警设备的性能边界,为家庭、工业、公共安防等领域提供高效、稳定的安全守护。一、技术内核:五大核心突破,构建全场景安防基座1. 双模音频传输,灵活应对复杂场景实时音频流传输:内置高灵敏度MIC,支持环境音实时采集,通过4G模块直接上传至云端服务器,响应速度低至毫秒级,适用于火灾警报、紧急呼救等需即
    广州唯创电子 2025-04-08 08:59 141浏览
  •   卫星图像智能测绘系统全面解析   一、系统概述   卫星图像智能测绘系统是基于卫星遥感技术、图像处理算法与人工智能(AI)技术的综合应用平台,旨在实现高精度、高效率的地理空间数据获取、处理与分析。该系统通过融合多源卫星数据(如光学、雷达、高光谱等),结合AI驱动的智能算法,实现自动化、智能化的测绘流程,广泛应用于城市规划、自然资源调查、灾害监测等领域。   应用案例   目前,已有多个卫星图像智能测绘系统在实际应用中取得了显著成效。例如,北京华盛恒辉和北京五木恒润卫星图像智能测绘系统
    华盛恒辉l58ll334744 2025-04-08 15:04 74浏览
  • 曾几何时,汽车之家可是汽车资讯平台领域响当当的“扛把子”。2005 年成立之初,它就像一位贴心的汽车小助手,一下子就抓住了大家的心。它不仅吸引了海量用户,更是成为汽车厂商和经销商眼中的“香饽饽”,广告投放、合作推广不断,营收和利润一路高歌猛进,2013年成功在纽交所上市,风光无限。2021年更是在香港二次上市,达到了发展的巅峰,当年3月15日上市首日,港股股价一度高达184.6港元,市值可观。然而,如今的汽车之家却陷入了困境,业务下滑明显。业务增长瓶颈从近年来汽车之家公布的财报数据来看,情况不容
    用户1742991715177 2025-04-07 21:48 113浏览
  • 文/Leon编辑/cc孙聪颖‍转手绢、跳舞、骑车、后空翻,就在宇树、智元等独角兽企业率领“机器人大军”入侵短视频时,却有资本和科技大佬向此产业泼了一盆冷水。金沙江创投管理合伙人朱啸虎近日突然对人形机器人发难,他表示“最近几个月正在批量退出人形机器人公司”。“只是买回去做研究的,或者买回去做展示的,这种都不是我们意义上的商业化,谁会花十几万买一个机器人去干这些活?”朱啸虎吐槽。不过,朱啸虎的观点很快就遭到驳斥,众擎机器人的创始人、董事长赵同阳回怼道:“(朱啸虎)甚至问出了人形机器人在这个阶段有什么
    华尔街科技眼 2025-04-07 19:24 150浏览
  • 文/郭楚妤编辑/cc孙聪颖‍伴随贸易全球化的持续深入,跨境电商迎来蓬勃发展期,物流行业 “出海” 成为不可阻挡的必然趋势。加之国内快递市场渐趋饱和,存量竞争愈发激烈。在此背景下,国内头部快递企业为突破发展瓶颈,寻求新的增长曲线,纷纷将战略目光投向海外市场。2024 年,堪称中国物流企业出海进程中的关键节点,众多企业纷纷扬帆起航,开启海外拓展之旅。然而,在一片向好的行业发展表象下,部分跨境物流企业的经营状况却不容乐观。它们受困于激烈的市场竞争、不断攀升的运营成本,以及复杂的国际物流环境,陷入了微利
    华尔街科技眼 2025-04-09 15:15 31浏览
  •     根据 IEC术语,瞬态过电压是指持续时间几个毫秒及以下的过高电压,通常是以高阻尼(快速衰减)形式出现,波形可以是振荡的,也可以是非振荡的。    瞬态过电压的成因和机理,IEC 60664-1给出了以下四种:    1. 自然放电,最典型的例子是雷击,感应到电力线路上,并通过电网配电系统传输,抵达用户端;        2. 电网中非特定感性负载通断。例如热处理工厂、机加工工厂对
    电子知识打边炉 2025-04-07 22:59 136浏览
  • 在全球电子产业面临供应链波动、技术迭代和市场需求变化等多重挑战的背景下,安博电子始终秉持“让合作伙伴赢得更多一点”的核心理念,致力于打造稳健、高效、可持续的全球供应链体系。依托覆盖供应商管理、品质检测、智能交付的全链路品控体系,安博电子不仅能确保电子元器件的高可靠性与一致性,更以高透明的供应链管理模式,助力客户降低风险、提升运营效率,推动行业标准升级,与全球合作伙伴共同塑造更具前瞻性的产业生态。动态优选机制:构建纯净供应链生态安博电子将供应商管理视为供应链安全的根基。打造动态优选管控体系,以严格
    电子资讯报 2025-04-07 17:06 95浏览
我要评论
0
0
点击右上角,分享到朋友圈 我知道啦
请使用浏览器分享功能 我知道啦