有时候你会发现某些文件在不停的增长严重占用了磁盘空间,或者删除某个大文件后发现磁盘空间没有被回收。 这说明该文件正在被某个进程所占用,你想找到罪魁祸首是谁,该怎么做呢?
涉及到的命令: fuser
本部分以archlinux系统为例
在archlinux上,fuser命令是属于 psmisc
包中的,因此运行下面命令安装:
sudo pacman -S psmisc --noconfirm
fuser的使用方法非常简单,只需要直接后接要查看的文件路径就行了。比如:
sleep 10 >/tmp/test &
fuser /tmp/test 2>&1
我们可以看到,fuser告诉我们进程 2405063
打开了 /tmp/test
这个文件,我们用 ps
来看一下
ps -elf |grep 2405063 |grep -v grep
若我们只想直接杀掉打开该文件的进程,那么可以直接使用 -k
选项(或者 --kill
选项):
sleep 10 >/tmp/test &
fuser -k /tmp/test 2>&1
ps -elf |grep sleep |grep -v grep
涉及到的命令: bash
本部分以archlinux系统为例
- 无需安装
我们知道 /proc/$pid/fd/
目录下存储着进程号为 $pid
的进程所打开的所有文件描述符号和对应的文件路径:
ls -l /proc/$$/fd/
所以我们可以通过遍历该目录下的所有文件描述符所指向的文件路径来找出打开文件的进程号:
首先, /proc/$pid/fd
目录中的软链接都是指向绝对路径的,因此我们需要定义一个函数获取指定文件的绝对路径:
function abs_path()
{
local file="$1"
local abs_dir="$(cd $(dirname $1);pwd)"
local basename="$(basename $1)"
echo "${abs_dir}/${basename}"
}
基于兼容性考虑,这里我们使用 dirname
命令和 basename
来获取文件路径中的目录和文件名,而没有使用bash的参数扩展功能来截取.
下一步我们定义一个函数来判断某个进程是否打开了指定文件:
function is_opened_file()
{
local pid="$1"
local file="$2"
for f in /proc/${pid}/fd/*
do
if [[ "$(readlink $f)" == "${file}" ]];then
return 0
fi
done
return 1
}
注意到,该函数没有去获取文件的绝对路径,这是基于性能考虑。因为我们需要遍历系统所有进程(通常有上千个)中打开的文件描述符来找出打开指定文件的进程,如果每次遍历都获取一次绝对路径那么性能消耗会非常严重,也是没有必要的。
最后,我们遍历整个 /proc
下的进程目录,找出打开指定文件的进程号:
function fileuser()
{
file="$(abs_path "$1")"
for pid in $(cd /proc;ls -d [0-9]*);
do
if is_opened_file "${pid}" "${file}";then
echo ${pid}
fi
done
}
现在我们来测试一下:
sleep 10 > /tmp/test &
fileuser /tmp/test
不过相比于第一个解决方案来说,这个解决方案特别慢,在我电脑上耗费了差不多5秒钟才出结果。
更多常见选项请运行 man fuser
或 fuser --help
查看帮助