用 lsof 查文件没结果,因默认只显示当前持有 fd 的进程;需加 sudo +l1 -pn 组合命令,或用 flock()、/proc/*/fd 扫描、mtime/inotifywait 辅助判断活跃写入。

用 lsof 查文件但没结果?默认不显示瞬态写入
直接运行 lsof /var/log/app.log 经常查不到正在写日志的进程,不是命令错了,而是 lsof 默认只抓“此刻正持有该文件 fd”的进程。但真实场景中:日志轮转后旧文件被 rename(),原进程没关 fd;或程序用 open(O_TRUNC) 覆盖写但缓冲区未刷盘,fd 已释放——这些都会导致 lsof 漏掉。
补救办法:
- 加
sudo:避免权限不足看不到其他用户进程 - 加
+L1:强制列出所有链接(含已 rename 但未 close 的) - 加
-Pn:禁用端口和服务名解析,提速并避免 DNS 卡顿 - 组合使用:
sudo lsof +L1 -Pn /var/log/app.log
flock() 检测是否被独占锁定(Linux/macOS)
比看 fd 更可靠的方式是尝试加一个非阻塞共享锁。如果失败且 errno == EWOULDBLOCK,说明有别的进程正以排他模式(LOCK_EX)持有该文件——这通常意味着它正在写入。
注意点:
- 必须先
open()文件拿到 fd,不能对路径直接调用flock() - 用
O_RDONLY打开即可,不需要写权限 - 检测完立刻
close(fd),不维持句柄 - 示例逻辑:
int fd = open("/var/log/app.log", O_RDONLY); flock(fd, LOCK_SH | LOCK_NB);,若返回 -1 且errno == EWOULDBLOCK,基本可断定被写入占用
Windows 下用 CreateFileA() 判断写入占用
Windows 没有 flock(),得靠打开时的共享策略反推。核心是:以 GENERIC_READ 打开,同时声明 FILE_SHARE_READ(不带 FILE_SHARE_WRITE)。如果失败且 GetLastError() == ERROR_SHARING_VIOLATION,说明当前有进程正以写方式打开该文件。
关键细节:
- 必须用
OPEN_EXISTING,不能用CREATE_ALWAYS或TRUNCATE_EXISTING - 哪怕只是读取目的,也别设
FILE_SHARE_WRITE,否则永远检测不到 - 成功返回句柄后务必
CloseHandle(),否则可能泄漏句柄 - 注意:记事本等工具写完即关句柄,这种瞬时占用容易漏判,需多次轮询
文件已删除但还在写?去 /proc/*/fd 翻底裤
当日志被 rm 删除,但进程仍在往它的 fd 写(常见于长期运行的服务),lsof 仍能显示 (deleted),但有时会因权限或过滤漏掉。最硬核的办法是手动扫 /proc:
执行:for pid in /proc/[0-9]*; do ls -l "$pid/fd" 2>/dev/null | grep "app.log.*deleted" && echo "PID: ${pid#/proc/}"; done
输出类似:lr-x------ 1 root root 64 Sep 8 10:22 5 -> /var/log/app.log (deleted),说明 PID 对应进程的 fd 5 正在写这个已删文件。
这个方法绕过所有抽象层,直接读内核维护的 fd 符号链接,可靠性最高,但速度慢、需 root 权限。
真正难的不是查到谁在写,而是判断“写入是否活跃”——比如一个进程每小时 flush 一次缓冲区,flock() 检测可能全绿,但文件其实正被持续追加。这时候得结合 stat -c "%y" /var/log/app.log 看 mtime 是否在变,再配合 inotifywait -m -e modify /var/log/app.log 实时监听事件。











