每个进程打开的文件数量统计

66

我正在开发一个在Linux中监控进程资源并周期性报告的应用程序,但是我在提取每个进程打开的文件计数时遇到了问题。

如果我将所有文件按其PID分组并计数,则需要相当长的时间。

如何获取Linux中每个进程的打开文件计数?


1
你需要使用 lsof 命令:man lsof - BMW
5个回答

130

看一下 /proc/ 文件系统:

ls /proc/$pid/fd/ | wc -l

要为所有进程执行此操作,请使用以下内容:

cd /proc
for pid in [0-9]*
do
    echo "PID = $pid with $(ls /proc/$pid/fd/ | wc -l) file descriptors"
done
作为一个单行命令(通过添加| grep -v "0 FDs"进行过滤):
for pid in /proc/[0-9]*; do printf "PID %6d has %4d FDs\n" $(basename $pid) $(ls $pid/fd | wc -l); done
作为一个一行命令,按文件描述符计数降序排序(通过在结尾添加 | head -10 限制结果数量):
for pid in /proc/[0-9]*; do p=$(basename $pid); printf "%4d FDs for PID %6d; command=%s\n" $(ls $pid/fd | wc -l) $p "$(ps -p $p -o comm=)"; done | sort -nr

感谢@Boban提供的这个补充:

你可以将上述脚本的输出导入下面的脚本,以查看打开最多文件描述符的十个进程(以及它们的名称):

  ...
done | sort -rn -k5 | head | while read -r _ _ pid _ fdcount _
do
  command=$(ps -o cmd -p "$pid" -hc)
  printf "pid = %5d with %4d fds: %s\n" "$pid" "$fdcount" "$command"
done

这是另一种列出打开文件描述符最多的前十个进程的方法,可能不太易读,因此我没有放在前面:

find /proc -maxdepth 1 -type d -name '[0-9]*' \
     -exec bash -c "ls {}/fd/ | wc -l | tr '\n' ' '" \; \
     -printf "fds (PID = %P), command: " \
     -exec bash -c "tr '\0' ' ' < {}/cmdline" \; \
     -exec echo \; | sort -rn | head

1
当然,对于许多进程,您需要具有root权限才能执行此操作。它们的文件描述符有点私密,你知道的;-) - Alfe
2
/proc/$pid/fd 列出了描述符文件,这与“打开文件”略有不同,因为我们可以拥有内存映射和其他不寻常的文件对象。 - mcoolive
1
这个命令可以将进程ID转换为命令名,并扩展答案:for pid in [0-9]*; do echo "PID = $pid with $(ls /proc/$pid/fd/ 2>/dev/null | wc -l) file descriptors"; done | sort -rn -k5 | head | while read -r line; do pid=echo $line | awk '{print $3}'; command=ps -o cmd -p $pid -hc; echo $line | sed -s "s/PID = \(.*\) with \(.*\)/Command $command (PID = \1) with \2/g"; done - Boban P.
1
嗯,与其解析原始输出,然后针对每个进程再次调用 ps 查找其命令,更有意义的做法可能是在第一个循环中使用 /proc/$pid/cmdline。虽然从技术上讲,在评估 [0-9]* 和扫描其磁盘之间,进程仍然有可能消失,但这种情况不太可能发生。 - Alfe
1
执行 command=$(ps -o cmd -p "$pid" -hc) 给我返回了 Warning: bad syntax, perhaps a bogus '-'。但是在运行时,使用 command=$(ps -o cmd -p "$pid" hc) 是可以正常工作的。 - Lucas Basquerotto
显示剩余9条评论

10

试试这个:

ps aux | sed 1d | awk '{print "fd_count=$(lsof -p " $2 " | wc -l) && echo " $2 " $fd_count"}' | xargs -I {} bash -c {}

对于 Fedora,它会给出以下警告: lsof: 警告:无法 stat() fuse.gvfsd-fuse 文件系统 /run/user/1000/gvfs 输出信息可能不完整。 lsof: UID 65535 没有 pwd 条目。 - Alexred

7

我使用了这个方法来查找给定用户(用户名)的顶级文件处理程序消耗进程,因为我没有lsof或root访问权限:

for pid in `ps -o pid -u username` ; do echo "$(ls /proc/$pid/fd/ 2>/dev/null | wc -l ) for PID: $pid" ; done  | sort -n | tail

6
如何在Linux中获取每个进程的打开文件数?
procpath query -f stat,fd

如果您是以root身份运行它的(例如,在命令前加上sudo -E env PATH=$PATH),否则它将仅返回每个进程的文件描述符计数,您可以列出其/proc/{pid}/fd。这将给您一个大的JSON文档/树,其节点看起来像:

{
  "fd": {
    "anon": 3,
    "blk": 0,
    "chr": 1,
    "dir": 0,
    "fifo": 0,
    "lnk": 0,
    "reg": 0,
    "sock": 3
  },
  "stat": {
    "pid": 25649,
    "ppid": 25626,
    ...
  },
  ...
}
fd字典的内容是每种文件描述符类型的计数。其中最有趣的可能是这些(有关更多详细信息,请参见procfile.Fd说明或man fstat):
  • reg - 打开(常规)文件的计数
  • sock - 打开套接字的计数
我是Procpath的作者,这是一个为进程分析提供更好界面的工具。您可以记录进程树的procfs统计信息(在SQLite数据库中),并稍后绘制任何一个。例如,这是我的Firefox进程树(根PID 2468)在打开文件描述符计数方面的情况(所有类型的总和):
procpath --logging-level ERROR record -f stat,fd -i 1 -d ff_fd.sqlite \ 
  '$..children[?(@.stat.pid == 2468)]'
# Ctrl+C
procpath plot -q fd -d ff_fd.sqlite -f ff_df.svg

Firefox打开的文件描述符

如果我只对某种类型的打开文件描述符(比如套接字)感兴趣,我可以像这样绘制它:

procpath plot --custom-value-expr fd_sock -d ff_fd.sqlite -f ff_df.svg

1
这对我来说有效:

此处为HTML代码

ps -opid= -ax | xargs -L 1 -I{} -- sudo bash -c 'echo -n "{} ";lsof -p {} 2>/dev/null | wc -l' | sort -n -k2

它按numopenfiles对每个pid进行排序打印。
它将要求输入sudo密码一次。
请注意,上述数字的总和可能大于所有进程的打开文件总数。
如我在这里阅读到的:forked processes can share file handles

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接