学习bcc的示例代码的记录与分析
from bcc import BPF
from time import sleep
b = BPF(text="""
#include <uapi/linux/ptrace.h>
#include <linux/sched.h>
struct key_t {
u32 prev_pid;
u32 curr_pid;
};
BPF_HASH(stats, struct key_t, u64, 1024);
int count_sched(struct pt_regs *ctx, struct task_struct *prev) {
struct key_t key = {};
u64 zero = 0, *val;
key.curr_pid = bpf_get_current_pid_tgid();
key.prev_pid = prev->pid;
// could also use `stats.increment(key);`
val = stats.lookup_or_try_init(&key, &zero);
if (val) {
(*val)++;
}
return 0;
}
"""
)
b.attach_kprobe(event_re="^finish_task_switch$|^finish_task_switch\.isra\.\d$",
fn_name="count_sched")
# generate many schedule events
for i in range(0, 100): sleep(0.01)
for k, v in b["stats"].items():
print("task_switch[%5d->%5d]=%u" % (k.prev_pid, k.curr_pid, v.value))
BPF是作为内核报文传输路径的一个旁路存在的,当报文到达内核驱动程序后,内核在将报文上送协议栈的同时,会额外将报文的一个副本交给BPF。之后,报文会经过BPF内部逻辑的过滤(这个逻辑可以自己设置) ,然后最终送给用户程序(比如tcpdump)。
函数int count_sched(struct pt_regs *ctx, struct task_struct *prev)中
参数struct pt_regs *ctx是寄存器和BPF文件
struct task_struct *prev 则是finish_task_switch运行时传入的参数的副本
bpf_get_current_pid_tgid的返回值为: current->tgid << 32 | current->pid
,高 32 位置为 tgid ,低 32 位为 pid(tid),由于curr_pid只有32位,按照c语言的赋值规则来说,curr_pid得到的是返回值的低32位,即当前进程的pid。
val = stats.lookup_or_try_init(&key, &zero);
if (val) {
(*val)++;
}
return 0;
lookup_or_try_init这个函数不是很懂,只能从名字上看出它是给hash表初始化或者插入的,其中key就是插入的关键字,zero应该是关键字对应的值,以及val这个值是干什么的也不清楚,只能大致猜测是hash表中元素的计数器。希望有大佬可以讲一下
b.attach_kprobe(event_re="^finish_task_switch$|^finish_task_switch\.isra\.\d$",
fn_name="count_sched")
这一句是通过attach_kprobe将finish_task_switch和count_sched关联起来,现在我一般写成
b.attach_kprobe(evente="finish_task_switch",fn_name="count_sched")
这两种写法有什么区别同求大佬解答
参考资料: