导读:本期聚焦于雪花创作的《如何使用bpf_get_current_task_mkdir获取mkdir系统调用的目录创建信息?》,敬请观看详情。在排查容器频繁创建临时目录导致磁盘抖动时,仅靠审计日志难以关联到具体进程上下文。eBPF提供了bpf_get_current_task_mkdir辅助函数,可在内核跟踪点触发时直接读取当前任务试图创建的目录路径。该函数从task_struct中提取fs_struct信息,避免了在用户态反复读取proc文件的开销。相比kprobe挂载sys_mkdir并手动回溯参数,它能稳定获取带命名空间视角的路径,且不受参数寄存器优化影响。实践中配合perf buffer可将创建事件实时投递到用户态做聚合,精准定位异常服务。

在Linux内核观测领域,eBPF已经成为系统调用级追踪的首选手段。当我们需要分析系统中所有mkdir行为背后的目录创建细节时,传统的审计子系统(auditd)虽然能记录路径,但无法便捷地关联调用进程的内核任务状态,也难以在低开销下做实时过滤。bpf_get_current_task_mkdir作为eBPF辅助函数,允许我们在挂载到mkdir相关跟踪点时,直接从当前任务的进程描述符中拿到即将创建的目录名,而不必依赖寄存器或栈上参数的不稳定抓取。

如何使用bpf_get_current_task_mkdir获取mkdir系统调用的目录创建信息?

bpf_get_current_task_mkdir的函数原理与数据结构

bpf_get_current_task_mkdir并不是标准上游内核长期稳定提供的辅助函数名,在多数发行版中它体现为通过bpf_get_current_task拿到task_struct指针后,由eBPF程序自行顺着fs指针读取fs_struct中的root与pwd,再结合系统调用参数中的路径名计算出绝对目录。理解这一点很关键:所谓“获取mkdir分析目录创建”,本质是借助当前任务上下文补全路径语义。task_struct中的fs字段指向fs_struct,其中保存了进程的根目录和当前工作目录的vfsmount与dentry,eBPF程序可以利用内核头文件生成的BTF信息直接偏移访问。

在mkdir系统调用入口处,用户传入的是相对或绝对路径字符串。如果直接读取该字符串,只能得到调用者视角下的路径片段,无法知道它映射到的真实目录树位置。通过当前任务的fs_struct,程序能把相对路径解析为挂载命名空间内的目标父目录。例如容器内的进程在/ tmp下创建目录,从宿主机命名空间看可能对应/var/lib/docker/overlay2/xxx/tmp,这种映射只有结合task的fs信息才能还原。

从性能角度看,在eBPF中直接读取task上下文比在用户态轮询/proc/PID/cwd要轻量得多。后者每次都要打开文件、读取链接、解析字符串,且容易遇到进程退出导致的竞态。而eBPF辅助调用在内核态同步完成,数据一致性由调度器保证。下面是一个简化版的BTF偏移读取示例,展示如何从当前任务提取父目录dentry名。

#include <linux/sched.h>
#include <linux/fs_struct.h>
#include <bpf/bpf_helpers.h>

SEC("tracepoint/syscalls/sys_enter_mkdir")
int trace_mkdir(struct trace_event_raw_sys_enter *ctx) {
    struct task_struct *task = (struct task_struct *)bpf_get_current_task();
    // 通过BTF生成的偏移访问task->fs->pwd.dentry
    struct fs_struct *fs = task->fs;
    struct dentry *pwd = fs->pwd.dentry;
    char buf[64];
    // 简化:读取dentry->d_name.name,实际需用bpf_probe_read_kernel
    bpf_probe_read_kernel(buf, sizeof(buf), pwd->d_name.name);
    bpf_printk("mkdir under cwd: %sn", buf);
    return 0;
}

在mkdir跟踪点中落地目录创建分析

将上文原理应用到mkdir分析时,我们通常选择sys_enter_mkdir或sys_enter_mkdirat跟踪点。这两个点分别对应mkdir和mkdirat系统调用,后者多一个目录文件描述符参数。在eBPF程序中,除了利用当前任务上下文拿到cwd对应的目录项,还要结合第一个参数(路径指针)读取用户传入的目录名。对于mkdirat,若dirfd为AT_FDCWD,则基于cwd解析;否则需从dirfd对应的file结构取目录,这在eBPF里可通过bpf_task_get_pid等辅助结合fdtable查找,但复杂度较高,一般建议只关注AT_FDCWD场景。

为了输出结构化的目录创建事件,我们可以在eBPF中定义结构体,包含pid、cwd字符串、目标路径字符串、时间戳,然后通过perf array或ring buffer发送到用户态。用户态程序负责聚合,比如统计每秒哪个容器创建目录最多。这种方案相比纯审计日志,延迟更低,且能利用eBPF地图做去重。下面的代码展示如何把路径与cwd拼接后提交事件。

struct mkdir_event {
    u32 pid;
    char cwd[128];
    char path[128];
};

SEC("tracepoint/syscalls/sys_enter_mkdir")
int trace_mkdir_enter(struct trace_event_raw_sys_enter *ctx) {
    struct mkdir_event e = {};
    e.pid = bpf_get_current_pid_tgid() >> 32;
    struct task_struct *task = (struct task_struct *)bpf_get_current_task();
    struct fs_struct *fs = task->fs;
    bpf_probe_read_kernel(&e.cwd, sizeof(e.cwd), fs->pwd.dentry->d_name.name);
    // 参数1为路径名指针
    bpf_probe_read_user(&e.path, sizeof(e.path), (void *)ctx->args[0]);
    bpf_perf_event_output(ctx, &events, BPF_F_CURRENT_CPU, &e, sizeof(e));
    return 0;
}

需要注意,读取用户态路径时必须使用bpf_probe_read_user系列函数,因为路径字符串位于进程地址空间。若进程在读取瞬间消亡,这类调用会安全返回错误而非崩溃内核。另外,dentry的d_name.name长度不固定,真实代码应循环读取或限制长度,避免缓冲区溢出。通过这些细节处理,目录创建分析才能稳定用于生产环境。

对比其他方案与常见误区

有些工程师习惯用kprobe挂载do_mkdirat函数,然后从寄存器提取struct path指针。这种方式在x86和arm架构下寄存器编号不同,且编译器优化可能把参数放进栈,导致eBPF读取失败。而基于tracepoint加bpf_get_current_task_mkdir式上下文读取,不依赖特定寄存器约定,可移植性更好。另外,auditd规则可能产生大量日志,磁盘IO反而成为瓶颈,eBPF在内核态过滤明显更优。

一个常见误区是认为“获取mkdir分析目录创建”只需要路径参数就够了。实际上,如果没有当前任务的fs上下文,短路径如“subdir”无法判断真实位置,尤其在chroot或容器环境下。另一个误区是直接在eBPF里做字符串拼接并printk,这会大量占用trace_pipe,正确做法是用地图批量传用户态处理。下面表格对比三种方案特点。

方案上下文获取开销稳定性
auditd用户态日志含cwd高(磁盘写)中(规则复杂漏报)
kprobe do_mkdirat需解析寄存器低(架构相关)
tracepoint加task读取直接task->fs极低高(不依赖寄存器)

综合来看,使用bpf_get_current_task_mkdir思路来追踪mkdir目录创建,既贴合内核数据结构真实布局,又能规避用户态轮询和寄存器解析的双重脆弱性。在编写这类程序时,应当充分借助libbpf与BTF,减少硬编码偏移,使观测工具能跨内核版本运行。

bpf_get_current_task_mkdireBPFmkdir修改时间:2026-08-18 00:22:33

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。