在 Linux 上获取正在使用文件的进程的 PID 最有效的方法是什么

在 Linux 上获取正在使用文件的进程的 PID 最有效的方法是什么

假设我有一个文件,我想按需查询该文件以检查当前正在访问的进程是否(已打开)。遍历所有 PID/proc/{PID}/fd似乎是资源密集型的,是否有其他方法可以完成相同的任务?

答案1

有一些工具可以为您做到这一点:fuser(仅输出 PID)和lsof(很多选项和精美的输出)。

遍历 /proc/{PID}/fd 中的所有 PID 似乎是资源密集型的

无论如何,这是 Linux 上唯一的方法。没有系统调用来查询哪些进程打开了文件。但不要自己这样做——因为/proc/{PID}/fd这只是严格意义上的打开文件,并且没有列出其他情况,例如内存映射文件、打开目录等。

请注意,这意味着您无法知道以其他用户身份运行的可能打开该文件的进程,即使您拥有该文件,除非您以 root 身份进行查找。 (但是,如果其他用户甚至无法看到该文件,并且自文件创建或系统重新启动以来一直如此,那么您就知道不存在这样的进程。)

一般地说,您无法控制的进程,其中可能包括其他情况,例如在不同命名空间中运行或具有额外权限的进程。

答案2

使用lsof

例如:

[root@nagios01 objects]# lsof /var/lib/mysql/ib_logfile0 
COMMAND   PID  USER   FD   TYPE DEVICE SIZE/OFF    NODE NAME
mysqld  17699 mysql    9uW  REG    8,3  5242880 4850251 /var/lib/mysql/ib_logfile0

另一个例子:

merlin@uc-s4m75657:~/Experiments/test_ifxr$ lsof -a .
COMMAND   PID   USER   FD   TYPE DEVICE SIZE/OFF   NODE NAME
bash     8646 merlin  cwd    DIR  254,2     4096 421348 .
bash     9261 merlin  cwd    DIR  254,2     4096 421348 .
ssh     37456 merlin  cwd    DIR  254,2     4096 421348 .
bash    38639 merlin  cwd    DIR  254,2     4096 421348 .
lsof    38679 merlin  cwd    DIR  254,2     4096 421348 .
lsof    38680 merlin  cwd    DIR  254,2     4096 421348 .

正如您所看到的,在这种情况下,“当前目录”(即点所指的位置)由六个进程声明。

最后一个例子还表明,它可能不是“PID”,而是同时存在多个 PID。另一个这样的例子是 PostgreSQL 数据文件,这些文件通常由多个工作进程同时读取。

请注意,如果您在编辑器中打开一个文件,则它不会显示在 lsof 输出中。例如,编辑器可以缓存该文件,并且仅在打开/保存操作期间触摸它,此时它将在 lsof 中看到;其他时间则不会。

答案3

根据我对以下问题的评论您是否需要检查可能未完全退出的进程?创建了一些测试代码来进行调查。

部分线程退出.adb是一个程序的源代码,它可以导致主线程退出,同时让另一个线程运行。

find_processes_using_file.c是类似程序的源代码lsof,可以扫描对文件名的引用。从命令行选项可以:

  1. 通过迭代按 PID 搜索/proc/<pid>/fd
  2. 通过迭代按 TID 搜索/proc/<pid>/task/<tid>/fd

启动了测试工具的两个实例partial_thread_exit,这使得一个名为/dev/shm/IBV_MESSAGE_BW_IN_USE.第一个实例的主线程和其他线程仍在运行:

$ ./partial_thread_exit 
Blocker_Task tid 17995 running in pid 17994
Main thread in pid 17994 - press return to exit main thread only

留下第二个实例,以便只有另一个线程正在运行,即主线程已退出:

$ ./partial_thread_exit 
Blocker_Task tid 13435 running in pid 13434
Main thread in pid 13434 - press return to exit main thread only

Main thread exiting...

如果运行按 PID 搜索,则无论是否以普通用户或 root 身份运行,都只会找到主线程正在运行的进程:

$ time ./find_processes_using_file /dev/shm/IBV_MESSAGE_BW_IN_USE
/proc/17994/fd/3 -> /dev/shm/IBV_MESSAGE_BW_IN_USE

num_filenames_compared=2532 num_eacces_errors=260

real    0m0.019s
user    0m0.003s
sys 0m0.015s
$ time sudo ./find_processes_using_file /dev/shm/IBV_MESSAGE_BW_IN_USE
/proc/17994/fd/3 -> /dev/shm/IBV_MESSAGE_BW_IN_USE

num_filenames_compared=3435 num_eacces_errors=0

real    0m0.044s
user    0m0.011s
sys 0m0.031s

而如果运行按 TID 搜索(通过使用该-t选项),则无论是否以普通用户或 root 身份运行,都会找到具有主线程的进程以及主线程已退出的进程:

$ time ./find_processes_using_file -t /dev/shm/IBV_MESSAGE_BW_IN_USE
/proc/13434/task/13435/fd/3 -> /dev/shm/IBV_MESSAGE_BW_IN_USE
/proc/17994/task/17994/fd/3 -> /dev/shm/IBV_MESSAGE_BW_IN_USE

num_filenames_compared=68327 num_eacces_errors=317

real    0m0.254s
user    0m0.036s
sys 0m0.217s
$ time sudo ./find_processes_using_file -t /dev/shm/IBV_MESSAGE_BW_IN_USE
/proc/13434/task/13435/fd/3 -> /dev/shm/IBV_MESSAGE_BW_IN_USE
/proc/17994/task/17994/fd/3 -> /dev/shm/IBV_MESSAGE_BW_IN_USE

num_filenames_compared=69500 num_eacces_errors=0

real    0m0.280s
user    0m0.048s
sys 0m0.228s

正如预期的那样,通过 TID 搜索比通过 PID 搜索慢,如下所示:

  1. 更实时报道time
  2. 报告的较大值num_filenames_compared是必须匹配的 PID/TID 打开的文件数量。

显然,两者之间的绝对差异将根据系统中运行的 TID/PID 总数而变化。

当以普通用户身份运行时,当尝试打开PID/TID 的目录失败并显示 时,的值num_eacces_errors不为零。即有多少次无权查找某个进程打开了哪些文件。fdEACCES

答案4

考虑到内核没有直接的方法,有一种非常原始的方法。在每个文件的存储文件系统中,应该创建可以是 pid 的进程引用和引用计数,这涉及修改 struct inode。然而,这将涉及修改打开系统调用和关闭系统调用,这将分别填充和消除引用和计数。 struct inode 当前维护时间戳,例如上次访问和上次修改时间戳,并且可以跟踪相同的时间戳来实现此目的。之后可以修改 fstat 等系统调用以返回此进程引用。这有一些缺点,例如必须修改文件系统和内核系统调用。根据您的目标(如果是嵌入式设备),这可能是可行的。通过打开和关闭调用进行的引用更改很可能需要同步。除非在极少数情况下,这绝对是不可行的。这还将涉及为此修补的内核升级。可能需要考虑一些因素,例如是否有太多进程正在访问文件,并且参考列表会很大。这些是与应用程序相关的问题。

相关内容