查找并删除已打开但已删除的大文件


119

如何找到已删除但仍在应用程序中打开的大文件?即使进程已打开,如何删除该文件?

情况是,我们正在运行一个进程,该进程正在以惊人的速度填充日志文件。我知道原因,并且可以解决。在此之前,我想在不关闭进程的情况下rm或清空日志文件。

简单地执行rm output.log操作只会删除对文件的引用,但是它将继续占用磁盘上的空间,直到进程终止。更糟糕:rming 之后,我现在无法找到文件的位置或文件的大小!有什么方法可以找到文件,甚至可以在另一个进程中打开它,也可以清空它?

我专门指的是基于Linux的操作系统,例如Debian或RHEL。


2
如果您知道该pid,则可以使用lsof -p <pid>它列出其打开的文件及其大小。删除的文件(deleted)旁边会有一个。删除的文件可能会链接到/proc/<pid>/fd/1。我不知道如何使进程停止写入其文件描述符而不终止它。我认为这将取决于过程。
donothing成功地

谢谢。如何获得所有rm仍打开的ed文件的PID ?
dotancohen

@donothingsuccessfully lsof报告的“ deleted”标记是特定于Solaris的,实际上仅是Solaris 10或更高版本。OP未指定他使用的操作系统。@dotancohen在Solaris上,您可以通过管道将lsof的输出通过管道搜索是否已删除,例如 lsof | grep "(deleted)"。当没有其他进程打开已删除文件时,内核将释放索引节点和磁盘块。进程没有“处理程序”,可以通过它们通知已打开的,本质上是锁定的文件已从磁盘中删除。
2013年

2
@Johan,lsof | grep '(deleted)'在Linux上也可以使用。在Linux上,你可以用inotify的机制(IN_DELETE_SELF活动)通知删除的档案(甚至已经没有任何的目录下的/ proc /一些-PID / FD以外的任何条目了文件)
斯特凡Chazelas

somefile在VIM中创建并打开了它,然后rm在另一个bash进程中对其进行了编辑。然后我运行lsof | grep somefile,即使文件在VIM中打开,它也不在其中。
dotancohen

Answers:


141

如果无法终止应用程序,则可以截断而不是删除日志文件来回收空间。如果未在附加模式(带有O_APPEND)下打开文件,则文件将与下一次应用程序向其写入文件时一样大(尽管前导部分稀疏并且看起来好像包含NUL字节),但是空格将被回收(这不适用于Apple OS / X上不支持稀疏文件的HFS +文件系统)。

截断它:

: > /path/to/the/file.log

如果已将其删除,则在Linux上,您仍然可以通过以下方式截断它:

: > "/proc/$pid/fd/$fd"

$pid打开文件的进程的进程ID 在哪里,打开$fd文件的文件描述符在哪里(您可以使用查看)lsof -p "$pid"

如果您不知道该pid,并且正在查找已删除的文件,则可以执行以下操作:

lsof -nP | grep '(deleted)'

lsof -nP +L1如@ user75021所述,它是一个更好的选择(更可靠,更可移植)(列出链接少于1个的文件)。

或(在Linux上):

find /proc/*/fd -ls | grep  '(deleted)'

或使用以下命令查找较大的zsh

ls -ld /proc/*/fd/*(-.LM+1l0)

如果应用程序是动态链接的,则另一种方法是将调试器附加到该应用程序并使其调用,close(fd)然后再调用new open("the-file", ....)


1
还有一个truncate命令可以更明确地执行相同的操作。
东武

1
@dotancohen Stephane编辑为包含有关在未知pid时如何执行此操作的信息。
Didi Kohen

1
@OlivierDulac,lsof可能是最接近列出打开文件的可移植解决方案的地方。在应用程序脚下关闭fd的调试器方法也应该很容易移植。
斯特凡Chazelas

2
@StephaneChazelas:谢谢。我找到了一种列出所有在每个分区上都有文件打开的PID的方法:(df -k | awk 'NR>1 { print $NF }' | xargs fuser -Vud 然后易于向违规者发送信号以迫使他们释放fd)
Olivier Dulac 2013年

6
您也可以使用lsof +L1。在lsof手册页中:“表单规范+L1将选择已取消链接的打开文件。表单规范+aL1 <file_system>将在指定的文件系统上选择未链接的打开文件。” 这应该比grepping更可靠。
同步2014年

30

在此处查看快速入门:lsof快速入门

我很惊讶没有人提到lsof quickstart文件(包含在lsof中)。“ 3.a”部分显示了如何查找打开的未链接文件:

lsof -a +L1 *mountpoint*

例如:

[root@enterprise ~]# lsof -a +L1 /tmp
COMMAND   PID   USER   FD   TYPE DEVICE    SIZE NLINK  NODE NAME
httpd    2357 apache   29u   REG 253,17 3926560     0  1499 /tmp/.NSPR-AFM-3457-9820130.0 (deleted)
mysqld   2588  mysql    4u   REG 253,17      52     0  1495 /tmp/ibY0cXCd (deleted)
mysqld   2588  mysql    5u   REG 253,17    1048     0  1496 /tmp/ibOrELhG (deleted)
mysqld   2588  mysql    6u   REG 253,17       0     0  1497 /tmp/ibmDFAW8 (deleted)
mysqld   2588  mysql    7u   REG 253,17       0     0 11387 /tmp/ib2CSACB (deleted)
mysqld   2588  mysql   11u   REG 253,17       0     0 11388 /tmp/ibQpoZ94 (deleted)
httpd    3457   root   29u   REG 253,17 3926560     0  1499 /tmp/.NSPR-AFM-3457-9820130.0 (deleted)
httpd    8437 apache   29u   REG 253,17 3926560     0  1499 /tmp/.NSPR-AFM-3457-9820130.0 (deleted)
httpd    8438 apache   29u   REG 253,17 3926560     0  1499 /tmp/.NSPR-AFM-3457-9820130.0 (deleted)
httpd    8439 apache   29u   REG 253,17 3926560     0  1499 /tmp/.NSPR-AFM-3457-9820130.0 (deleted)
httpd    8440 apache   29u   REG 253,17 3926560     0  1499 /tmp/.NSPR-AFM-3457-9820130.0 (deleted)
httpd    8441 apache   29u   REG 253,17 3926560     0  1499 /tmp/.NSPR-AFM-3457-9820130.0 (deleted)
httpd    8442 apache   29u   REG 253,17 3926560     0  1499 /tmp/.NSPR-AFM-3457-9820130.0 (deleted)
httpd    8443 apache   29u   REG 253,17 3926560     0  1499 /tmp/.NSPR-AFM-3457-9820130.0 (deleted)
httpd    8444 apache   29u   REG 253,17 3926560     0  1499 /tmp/.NSPR-AFM-3457-9820130.0 (deleted)
httpd   16990 apache   29u   REG 253,17 3926560     0  1499 /tmp/.NSPR-AFM-3457-9820130.0 (deleted)
httpd   19595 apache   29u   REG 253,17 3926560     0  1499 /tmp/.NSPR-AFM-3457-9820130.0 (deleted)
httpd   27495 apache   29u   REG 253,17 3926560     0  1499 /tmp/.NSPR-AFM-3457-9820130.0 (deleted)
httpd   28142 apache   29u   REG 253,17 3926560     0  1499 /tmp/.NSPR-AFM-3457-9820130.0 (deleted)
httpd   31478 apache   29u   REG 253,17 3926560     0  1499 /tmp/.NSPR-AFM-3457-9820130.0 (deleted)

在Red Hat系统上以查找快速启动文件的本地副本,我通常这样做:

[root@enterprise ~]# locate -i quickstart |grep lsof
/usr/share/doc/lsof-4.78/00QUICKSTART

... 或这个:

[root@enterprise ~]# rpm -qd lsof
/usr/share/doc/lsof-4.78/00.README.FIRST
/usr/share/doc/lsof-4.78/00CREDITS
/usr/share/doc/lsof-4.78/00DCACHE
/usr/share/doc/lsof-4.78/00DIALECTS
/usr/share/doc/lsof-4.78/00DIST
/usr/share/doc/lsof-4.78/00FAQ
/usr/share/doc/lsof-4.78/00LSOF-L
/usr/share/doc/lsof-4.78/00MANIFEST
/usr/share/doc/lsof-4.78/00PORTING
/usr/share/doc/lsof-4.78/00QUICKSTART
/usr/share/doc/lsof-4.78/00README
/usr/share/doc/lsof-4.78/00TEST
/usr/share/doc/lsof-4.78/00XCONFIG
/usr/share/man/man8/lsof.8.gz

1

文件系统驱动程序实际上要释放分配的空间,通常只有在释放所有引用该文件的文件描述符后,才会发生这种情况。因此,除非您使应用程序关闭文件,否则您无法真正回收空间。这意味着终止它或在调试器中“稍微”播放它(例如,关闭文件并确保不再打开/写入该文件,或者/dev/null改为打开它)。或者您可以破解内核,但是我建议您不要这样做。

按照Stephane的建议截断文件可能会有所帮助,但实际结果还取决于您的文件系统(例如,无论如何,仅在关闭文件后,可能才会释放预分配的块)。

此行为背后的基本原理是,内核不知道如何处理针对此类文件的数据请求(包括读取和写入,但读取实际上更为关键)。


2
由于Linux 在大多数文件系统上都支持稀疏文件,因此行为是明确定义的,并且磁盘驱动程序可以真正释放磁盘空间。我已经对ext3和ext4进行了测试,它的工作就像Stephane所写的那样。
jofel

1
是什么让您说截断文件不会收回预分配的块?截断是为了释放数据,我认为这没有任何歧义。
斯特凡Chazelas

1
文件系统可以保留分配的块以节省以后的时间(尤其是如果文件仍保持打开状态),尤其是在截断之前足够大的情况下。至少XFS似乎正在这样做。
彼得

谢谢彼得。我很高兴您解决本文中的“为什么”。
dotancohen

2
据我所知,截断打开的文件也确实会回收XFS上的空间。已对正常文件和fallocate在Linux 4.9上分配的文件进行了测试。您能否说明在什么文件系统和条件下截断文件不会回收空间?
斯特凡Chazelas
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.