如何用bpftrace打印tracepoint调用进程的父进程树?
针对sys_enter_fchownat遍历进程树并打印进程链信息
核心实现思路
在eBPF中,要遍历进程树需从当前进程的task_struct出发,循环访问real_parent指针直到根进程(或达到预设深度限制),同时获取每个进程的PID(用户态视角的tgid)和进程名称comm。
可行的eBPF代码示例
#include <vmlinux.h> #include <bpf/bpf_helpers.h> #include <bpf/bpf_tracing.h> #define MAX_TREE_DEPTH 16 // 限制遍历深度,避免栈溢出或异常循环 SEC("tracepoint/syscalls/sys_enter_fchownat") int trace_fchownat_entry(struct trace_event_raw_sys_enter *ctx) { struct task_struct *curr_task; struct task_struct *parent_task; int traverse_depth = 0; // 获取当前触发tracepoint的进程task_struct curr_task = (struct task_struct *)bpf_get_current_task(); if (!curr_task) return 0; bpf_printk("进程调用链:"); // 循环遍历父进程链 while (curr_task && traverse_depth < MAX_TREE_DEPTH) { u32 user_pid = curr_task->tgid; // tgid对应用户态看到的PID char comm_buf[TASK_COMM_LEN]; // 读取进程名称,也可直接访问curr_task->comm __builtin_memcpy(comm_buf, curr_task->comm, sizeof(comm_buf)); bpf_printk("[PID: %d, 进程名: %s]", user_pid, comm_buf); // 获取真正的父进程(避免线程组内部的leader干扰) parent_task = curr_task->real_parent; // 终止条件:父进程为空,或父进程等于自身(如init进程) if (!parent_task || parent_task == curr_task) break; curr_task = parent_task; traverse_depth++; } return 0; } char _license[] SEC("license") = "GPL";
关键注意事项
- 合法获取task_struct:必须使用
bpf_get_current_task()获取当前进程的结构体指针,不能自行通过PID偏移访问,否则会被BPF验证器拦截。 - 区分tgid和pid:用户态的PID对应内核
task_struct的tgid,而pid字段是线程ID,需打印tgid才符合用户认知。 - 使用real_parent:优先用
real_parent而非parent,后者可能指向线程组的领头线程,无法获取真正的父进程。 - 限制遍历深度:设置
MAX_TREE_DEPTH防止因异常进程结构导致的无限循环,同时避免BPF栈溢出。
你之前尝试的问题点
$pid_ns[$current_pid]->parent属于非法操作:BPF不允许直接通过PID数组访问父进程,必须通过task_struct指针遍历。task_struct_ptr->parent->pid的问题:一是获取task_struct_ptr的方式不符合BPF规范,二是pid字段是线程ID而非用户态PID,且直接访问指针可能触发验证器错误。
内容的提问来源于stack exchange,提问作者Ren Hoek
相关产品推荐
相关产品推荐

