温馨提示×

如何监控Debian readdir调用

小樊
42
2026-06-30 21:31:41
栏目: 智能运维

在 Debian(或其他 Linux)系统上监控 readdir(或其底层实现的 getdents / getdents64 系统调用)的调用情况,通常有几种不同层次、不同目的的做法。下面从常用到进阶给你一个比较完整的说明。


一、理解:readdir 并不是系统调用

glibc 里,readdir(3)用户态函数,底层实际使用的是:

  • getdents(32 位)
  • getdents64(64 位)

因此:

  • 监控 readdir:通常监控 getdents / getdents64
  • 监控库函数 readdir():需要用户态追踪(如 ltrace)

二、快速方式:使用 strace(最常用 ✅)

1️⃣ 实时监控某个进程的 readdir 行为

strace -e trace=getdents,getdents64 -p <PID>

或者启动一个新进程:

strace -e trace=getdents,getdents64 ls /

示例输出:

getdents64(3, /* 18 entries */, 32768) = 496
getdents64(3, /* 0 entries */, 32768)  = 0

2️⃣ 查看 readdir 正在读取哪个目录

fd 是关键:

strace -e trace=getdents,getdents64 -y ls /

输出示例:

getdents64(3</proc/self/fd>, /* 5 entries */, 32768) = 100

三、使用 ltrace(监控 readdir 函数本身)

如果你想直接看到 readdir() 函数调用(而不是系统调用):

ltrace -e readdir ls /

⚠ 注意:

  • ltrace 对静态链接程序无效
  • 对部分 glibc 优化可能不完整

四、系统级监控(所有进程)

1️⃣ 使用 auditd(推荐用于安全/审计)

安装

sudo apt install auditd

监控所有 getdents 系统调用

sudo auditctl -a always,exit -S getdents -S getdents64 -F arch=b64 -k readdir_monitor

查看日志

sudo ausearch -k readdir_monitor

示例:

type=SYSCALL msg=audit(...): arch=c000003e syscall=217 success=yes

⚠ 注意:

  • auditd 不能直接告诉你目录路径
  • 只能知道“某进程调用了 getdents”

五、使用 bpftrace / eBPF(高级 & 高性能 ✅)

1️⃣ 安装 bpftrace

sudo apt install bpftrace

2️⃣ 监控 getdents64 调用

sudo bpftrace -e '
tracepoint:syscalls:sys_enter_getdents64 {
    printf("pid=%d comm=%s fd=%d\n", pid, comm, args->fd);
}'

3️⃣ 尝试解析目录名(进阶)

sudo bpftrace -e '
tracepoint:syscalls:sys_enter_getdents64 {
    printf("pid=%d %s fd=%d\n", pid, comm, args->fd);
    // 可结合 bpf_probe_read 读取 fd 对应路径(复杂)
}'

✅ 优点:

  • 几乎无性能损耗
  • 可实时、系统级

六、从 /proc 反查 readdir 正在读哪个目录

配合 strace 或 bpftrace,看到 fd 后:

ls -l /proc/<PID>/fd/<fd>

或:

readlink /proc/<PID>/fd/<fd>

七、调试自己的程序(源码级)

如果你是开发者:

1️⃣ 使用 gdb

gdb ./your_program
break readdir
run

2️⃣ 使用 LD_PRELOAD 拦截 readdir

#define _GNU_SOURCE
#include <dirent.h>
#include <dlfcn.h>
#include <stdio.h>

struct dirent *readdir(DIR *dirp) {
    static struct dirent *(*real_readdir)(DIR *) = NULL;
    if (!real_readdir)
        real_readdir = dlsym(RTLD_NEXT, "readdir");

    struct dirent *entry = real_readdir(dirp);
    if (entry)
        fprintf(stderr, "readdir: %s\n", entry->d_name);
    return entry;
}

编译:

gcc -shared -fPIC -o readdir_hook.so readdir_hook.c -ldl

使用:

LD_PRELOAD=./readdir_hook.so ls

八、总结对比

方法 能看到什么 是否推荐
strace 系统调用级 ✅✅✅
ltrace readdir 函数
auditd 系统级审计 ✅(安全)
bpftrace 高性能系统级 ✅✅✅
gdb 调试
LD_PRELOAD 自定义拦截 ✅(开发)

九、如果你愿意,我可以帮你:

  • ✅ 写一个 bpftrace 脚本直接输出目录路径
  • ✅ 区分 readdir vs getdents
  • ✅ 针对 某个进程 / 某个目录 做精准监控
  • ✅ 分析 readdir 频繁调用导致的性能问题

你可以告诉我:

是想排查性能问题、做安全审计,还是调试自己的程序?

0