在 Linux 系统中,默认不会自动把文件读取行为记录成日志。也就是说,像“谁在什么时间访问了哪个文件”这类文件访问记录,通常只有在你提前部署并启用 auditd、bpftrace 等审计工具后,才有可能被完整保留下来。至于 stat 命令里看到的 Access 时间,也不能直接当作可靠依据:一方面,relatime、noatime 等挂载选项很可能让这个时间戳失去参考意义;另一方面,它本身也无法提供用户、进程、命令等审计层面的关键信息。

stat 命令看到的 Access 时间并不是可靠的访问证据,ls -lu 同样不够准确;inotifywait 只能捕获一部分文件打开或读取行为;如果真的需要追踪 Linux 文件访问记录,通常还是要依赖 auditd 或 bpftrace。
为什么 stat /path 显示的 Access 时间不可信
内核维护的 atime 字段,理论上表示文件最后一次访问时间,但在实际生产环境中,这个字段的参考价值往往非常有限:
- 大多数 Linux 系统在挂载文件系统时会使用
relatime(默认)或noatime,因此atime不会随着每一次读操作都更新 - 即使启用了
strictatime,它也只能说明“这个文件至少被读取过一次”,并不会记录访问次数、进程信息、用户名,也无法区分是cat、grep还是dd发起的读取 - 像
dd if=/dev/sda of=/dev/null这种绕过 VFS 的块设备读取方式,atime完全不会发生变化 stat输出中的Access:行,本质上只是 inode 中该字段当前值的快照,并不是可以直接作为审计依据的访问日志
auditctl 监控单个文件读取的实操要点
如果你想在 Linux 中查看某个文件的访问记录,auditd 通常是生产环境中最稳妥、最可落地的方案之一,但前提是必须手动添加规则并做好持久化配置:
- 先确认服务是否运行:
sudo systemctl is-active auditd,如果未运行,则执行sudo systemctl enable --now auditd - 添加临时规则(系统重启后会失效):
sudo auditctl -w /etc/shadow -p r -k shadow_read,其中-p r表示只监控读取操作 - 查询访问记录可使用:
sudo ausearch -k shadow_read | aureport -f -i,输出结果通常包含 UID、可执行文件路径以及命令行参数等信息 - 如果希望规则永久生效,就把它写入
/etc/audit/rules.d/shadow.rules,内容保持一致,然后执行sudo augenrules --load - 要特别注意日志膨胀问题:
/var/log/audit/audit.log可能因为高频读取而迅速变大,因此不要轻易对/proc或日志轮转目录配置-p r监控
bpftrace 抓 read 系统调用的轻量替代方案
如果你觉得 auditd 太重,或者当前系统权限限制较多,那么 bpftrace 往往是更灵活、也更接近底层的一种替代方案。不过前提是 Linux 内核必须支持 eBPF,才能正常使用这类跟踪能力:
- 统计各进程对 fd 的读取频次(不包含文件路径):
sudo bpftrace -e 'tracepoint:syscalls:sys_enter_read { @reads[comm, args->fd] = count(); } interval:s:5 { print(@reads); clear(@reads); }' - 如果想进一步关联文件路径,eBPF 里并不能直接查询
/proc/PID/fd/,通常需要先找出高频出现的comm+fd组合,再手动结合lsof -p PID或readlink /proc/PID/fd/FD_NUM进行反查 - 为了避免性能波动,不建议在 tracepoint 中做大量字符串拷贝或频繁
printf,更推荐使用@count这类聚合方式 - 对于旧版本内核兼容性较差:通常建议在 5.10+ 内核环境下再考虑使用
struct file做路径推导,而更早版本则更适合专注于 fd + comm 的读取统计
inotifywait 只适合短期、低频、已知文件的监听
inotifywait 本质上不是 Linux 审计工具,而是文件事件通知机制,因此误报和漏报都比较常见,只适合短期调试,或者监看极少数明确的关键配置文件:
- 启动监听方式:
inotifywait -m -e access /etc/hosts 2>/dev/null,每次触发时会输出一行/etc/hosts ACCESS - 它依赖
IN_ACCESS事件,而该事件通常只有在 open(O_RDONLY) 之后又实际发生 read 时才会触发,像 mmap、sendfile、/proc读取等场景都不会触发 - 当多个进程并发读取同一个文件时,容易漏掉中间事件;而轮询脚本(例如每秒执行一次 cat)则可能导致事件堆积甚至丢失
- 它无法提供 UID、PID、命令行等信息,甚至连访问者到底是 root 还是普通用户都无法准确区分
