我遇到了一个问题,可能是由于服务打开了太多文件造成的。我试图检查它打开了哪些文件,并监控它随时间的变化。这是在 CentOS 7 上,所以我猜lsof
是我的朋友 - 但是,我无法理解结果。
我的服务以富有想象力的“service_user”名称运行。它是该机器上唯一一个以该名称运行的进程。
我假设使用lsof -u service_user
会给我该用户打开的文件,我只需计算它们(最初,然后可能检查详细信息)。这里的数字对于服务来说是合理的,当然不表示存在问题。但是,如果我进行全面lsof
过滤,只保留提及 service_user 的文件,我会得到三个数量级以上的文件!
[root ~]# lsof -u service_user | wc -l
1442
[root ~]# lsof | grep service_user | wc -l
1631673
我想看看机器上每个进程和用户的总数(以确认我的进程是否是罪魁祸首)并尝试对输出进行一些简单的操作,lsof
但如果使用了标志或未使用了标志,则显示的列会有所不同-u
- 完整lsof
包含一个time
通常是空白的列,并且使提取列变得棘手。
我尝试使用 -F 标志来指定列,并编写了以下脚本,我将输出导入其中,lsof -F pcLf
以便尝试将其处理为我可以处理的内容。这些结果与数字大致一致- 比结果lsof | grep service_user
大三个数量级lsof -u service_user
#!/bin/bash
IFS=""
echo -e "PID\tCommand\tUser\tNumber of Files"
OUTPUT=
N=0
function add_tab() {
OUTPUT+=$(echo -e "\t")
}
function write_out_and_reset() {
if [[ -n "$OUTPUT" ]]; then
echo "$OUTPUT$N"
fi
OUTPUT=
N=0
}
function add_value_to_output() {
local LINE="$1"
OUTPUT+="${LINE:1}"
add_tab
}
while read -r LINE || [ -n "$LINE" ]; do
case "${LINE:0:1}" in
'p')
write_out_and_reset
add_value_to_output "$LINE"
;;
'c')
add_value_to_output "$LINE"
;;
'u')
add_value_to_output "$LINE"
;;
'L')
add_value_to_output "$LINE"
;;
'f')
N=$((N+1))
;;
*)
echo
echo "Unknown line $LINE"
;;
esac
done
write_out_and_reset
问题
- 如何才能可靠地找出给定进程或用户打开了多少个文件?
- 如何才能可靠地获得每个用户/进程在机器上打开的所有文件的总和?
- 如何可靠地列出特定用户或进程打开的所有文件?
我也想了解为什么我会得到如上所述的不同结果,但主要问题是应该相信哪一个,这样我才能继续调查我的实际问题。
答案1
打开文件的限制是针对每个进程的,因此我认为计算每个用户的打开文件数是没有意义的 - 特定的守护进程要么达到其限制,要么没有。
要查看特定进程的打开的文件,请使用以下方法之一:
ls -l /proc/<pid>/fd
lsof -p <pid> -d fd -a -n
lsfd -p <pid> -Q "FD >= 0"
(lsfd 在 CentOS 7 上不存在,但可以在较新的发行版中找到)
要按用户列出它们,请使用:
lsof -u <user> -d fd -a -n
lsfd -Q "(USER == '<user>') && (FD >= 0)"
但请注意,lsof 和 lsfd 还显示各种其他资源,例如内存映射,不要计算打开文件数限制。您只需要计算文件描述符。
但是,如果我使用完整的 lsof 并过滤掉那些提到 service_user 的文件,我会得到多三个数量级的文件!
如果没有-u
过滤器,lsof 也会报告每个线每个进程的 - 这会导致重复输出,因为进程中的所有线程共享相同的文件描述符和大多数其他资源(特殊情况除外)。因此,如果一个进程有 10 个线程(包括主线程),lsof
则会报告相同的文件描述符 10 次。添加-Ki
选项以避免这种情况。