如何用eBPF代码从cgroup v1中获取容器ID?(task_struct尝试失败)
在eBPF中通过task_struct获取cgroup v1容器ID的解决方法
先排查task_struct->cgroups为0的问题
- 内核结构定义不匹配:你用的
task_struct定义可能和目标内核版本的实际结构不一致,导致cgroups字段的偏移错误,读出来的值为0。解决方法:用bpftool btf dump file /sys/kernel/btf/vmlinux format c | grep -A 30 'struct task_struct'获取内核真实的task_struct结构,确保代码中cgroups字段的位置正确;或者使用bpf_core_read系列函数安全读取字段,避免硬编码偏移(推荐)。 - 内核配置问题:确认内核开启了
CONFIG_CGROUPS选项,cgroup v1依赖该配置,关闭的话task_struct->cgroups会始终为0。
正确的容器ID获取流程(cgroup v1)
容器ID通常嵌入在cgroup的路径中(比如Docker的/docker/<container-id>),需要从task关联的cgroup中解析路径:
核心eBPF代码示例(适配5.x内核,需根据实际内核调整结构体)
#include <linux/bpf.h> #include <bpf/bpf_helpers.h> #include <linux/sched.h> // 仅定义需要的结构体字段,避免全量定义导致偏移错误 struct task_struct { struct cgroup __rcu *cgroups; }; struct cgroup { struct cgroup_subsys_state *dfl_cgrp; struct dentry *dentry; }; struct cgroup_subsys_state { struct cgroup *cgrp; }; struct dentry { struct qstr d_name; struct dentry *d_parent; }; struct qstr { const unsigned char *name; unsigned int len; }; SEC("tracepoint/sched/sched_process_exec") int trace_exec(struct trace_event_raw_sched_process_exec *ctx) { struct task_struct *task = (struct task_struct *)bpf_get_current_task(); struct cgroup *cgrp; struct dentry *dentry; char path[256] = {0}; int pos = sizeof(path) - 1; // 安全读取cgroups字段 struct cgroup *cgroups; bpf_core_read(&cgroups, sizeof(cgroups), &task->cgroups); if (!cgroups) return 0; // 获取默认cgroup的css struct cgroup_subsys_state *css; bpf_core_read(&css, sizeof(css), &cgroups->dfl_cgrp); if (!css) return 0; // 获取cgroup结构体 bpf_core_read(&cgrp, sizeof(cgrp), &css->cgrp); if (!cgrp) return 0; // 获取cgroup对应的dentry bpf_core_read(&dentry, sizeof(dentry), &cgrp->dentry); if (!dentry) return 0; // 遍历dentry拼接路径 struct dentry *curr = dentry; while (curr && curr->d_parent != curr) { struct qstr name; bpf_core_read(&name, sizeof(name), &curr->d_name); if (pos - name.len - 1 < 0) break; pos -= name.len; bpf_core_read(path + pos, name.len, name.name); pos -= 1; path[pos] = '/'; bpf_core_read(&curr, sizeof(curr), &curr->d_parent); } // 解析Docker容器ID(根据实际运行时调整路径匹配规则) char *docker_prefix = __builtin_strstr(path, "/docker/"); if (docker_prefix) { char *container_id = docker_prefix + 8; // 跳过"/docker/" bpf_printk("Container ID: %s", container_id); } return 0; } char _license[] SEC("license") = "GPL";
关键注意事项
- 内核兼容性:不同内核版本的cgroup结构体字段名/偏移可能变化,必须用
bpf_core_read(依赖BTF)或者动态获取偏移,避免硬编码。 - 容器运行时适配:不同容器运行时的cgroup路径不同,比如containerd的路径是
/containerd/io.containerd.runtime.v1.linux/moby/<id>,需要调整路径匹配逻辑。 - 权限与内核参数:运行eBPF程序需要
CAP_PERFMON/CAP_BPF权限,非特权用户需确保kernel.unprivileged_bpf_disabled=0。
内容的提问来源于stack exchange,提问作者谭瑞星
相关产品推荐
相关产品推荐

