Contents

查找在Linux中使用文件的进程

1. 简介

有时当我们尝试访问文件时,我们可能会遇到文件忙的消息。这意味着系统正在运行一个正在使用该文件的进程,使其保持打开状态以进行读取或写入。发生这种情况时,有时我们会想要发现使用该文件的进程。

在本教程中,我们将了解如何找到正在使用文件的进程。

2. 查找进程的方法和命令

有几个命令可以帮助我们找到对文件进行操作的进程,所以我们将从那里开始。这些命令从 Linux 内核收集数据,因为它负责运行进程、文件系统等。此外,我们将直接读取内核表以获取所需的信息。

2.1. fuser器命令

让我们从列出使用文件或套接字的进程的fuser 命令开始。它也可以用来杀死一个进程。我们可以将它与*-v*参数一起使用来获得详细的输出:

$ fuser -v text.txt 
                     USER        PID ACCESS COMMAND
/home/john/text.txt:
                     john      22829 f....  less

正如我们所看到的,在这种情况下,less 的进程正在访问文件。** fuser命令返回*PID *、调用进程的用户和文件状态**。

运行带有*-k选项的命令将终止 它找到的进程。让我们尝试使用SIGKILL 杀死less*进程,使用 PID 24815:

$ fuser -k text.txt
/home/john/text.txt:  24815 

假设vi 正在访问同一个文件。当我们运行相同的命令时,不会返回任何内容,因为vi打开文件,将其内容读入内存,然后关闭它。内核已经完成了它的工作,所以关于文件的信息不可用。

但是,我们可以尝试通过分析和猜测fuser -cv text.txt的输出来找到过程。响应是访问同一文件系统上的文件的所有进程的列表。在这种情况下,输出的最后一行是我们正在寻找的进程。然而,情况可能并非总是如此。

$ fuser -cv text.txt  
                     USER        PID ACCESS COMMAND
/home/john/text.txt:
                     root     kernel mount /home
...
                     john     24807 F.c.. vi

运行带有*-k*选项的命令可能会杀死 所有使用指定文件或目录的进程,因此请谨慎使用。

2.2. lsof命令

lsof 命令可以返回打开文件的列表。为了缩小结果范围并保留标题行,我们将它与*head grep 命令一起使用。假设vi*仍在运行,让我们试一试:

$ lsof | { head -1 ; grep text.txt ; }
COMMAND     PID   TID TASKCMD               USER   FD      TYPE             DEVICE  SIZE/OFF       NODE NAME
vi        24807                            john     4u      REG                8,3     12288    3147621 /home/john/.text.txt.swp

** lsof命令返回进程名称、PID和运行该进程的用户**。如果进程有线程,我们将使用 task 命令查看它们的标识号TID 。FD 字段可以包含三个部分:文件描述符(在我们的例子中为4)是第一部分,模式字符是第二部分(u表示文件可读写),锁定字符是第三部分。

让我们看看当less 命令访问文件而不是vi时的输出:

$ lsof | { head -1 ; grep text.txt ; }
COMMAND     PID   TID TASKCMD               USER   FD      TYPE             DEVICE  SIZE/OFF       NODE NAME
less      28423                            john     4r      REG                8,3        75    3146117 /home/john/text.txt

在这种情况下,我们看到文件已打开以供读取,其中FD = 4r

但是,就像fuser一样,如果我们使用vi来编辑文件,lsof不会将其显示为正在使用。

该命令有很多选项。例如,-t只给出没有标头的进程标识符,这有助于编写脚本。

lsoffuser非常相似,只是它不能终止进程。然而,因为lsof也给了我们PID,我们可以用kill命令加入它:

$ kill -TERM `lsof -t text.txt`

要使用目录及以下目录中的文件发现所有进程的*PID ,我们可以使用 *+D 递归扫描它。

此外,lsof经常用于查找由给定(由PID)进程打开的所有文件:

$ lsof -p <PID>

2.3. 直接从内核获取信息

另一种检测正在使用的文件进程的方法是直接访问内核。内核将数据保存在/proc 下。有关进程的信息位于目录*/proc/pid_the_process*中。它包含进程文件打开的所有内容的条目,由其文件描述符命名,链接到实际文件。

因此,我们只需要使用ls 命令:

ls -l /proc/*/fd

让我们使用仅打印PID的脚本来改进这些结果:

#!/usr/bin/bash
for pid in /proc/{0..9}*; do
  i=$(basename "$pid")
  for file in "$pid"/fd/*; do
    link=$(readlink -e "$file")
    if [ "$link" ]; then
      echo "PID $i: $link"
    fi
  done
done | grep $1

对于dir /proc/pid中的每个PID,我们正在挖掘目录,然后挖掘到子目录fd。然后我们阅读链接。如果这个链接是一个文件,我们打印它的名字。最后,使用给定的文件名 grep 结果。

如果我们将此脚本保存为mylsof并允许它执行(chmod u+x mylsof),我们可以使用它来解决我们的问题:

$ ./mylsof text.txt
PID 30069: /home/john/text.txt

从内核获取信息总是有效的,即使在带有BusyBox 的系统上也是如此。如果系统中既没有lsof也没有fuser,它会有所帮助。