因此,长话短说,我编写了一个(python)程序,它打开了很多文件,在其中写入数据,然后删除了这些文件,但没有正确关闭文件句柄。一段时间后,该程序由于磁盘空间不足而停止。
bash 中的自动完成失败cannot create temp file for here-document: No space left on device"
并lsof -nP +L1
显示大量不再存在的文件。
杀死我的程序后,所有文件句柄都被关闭,磁盘空间再次“空闲”,一切都很好。
为什么会发生这种情况?磁盘空间并未物理填满。或者文件句柄的数量是否有限?
答案1
在 Unix 中删除文件只是删除对其数据的命名引用(因此系统调用名称为unlink
/ unlinkat
,而不是delete
)。为了释放数据本身,不得有其他对其的引用。可以通过以下几种方式获取参考:
- 文件系统上不得再引用此数据(
st_nlink
必须为 0)——硬链接时可能会发生这种情况。否则,当我们仍然可以从文件系统访问数据时,我们就会删除数据。 - 打开的文件句柄中不能进一步引用此数据(在 Linux 上,内核中的相关 必须为 0)
struct file
。f_count
否则,数据仍然可以通过读取或写入文件句柄(或/proc/pid/fd
在 Linux 上)来访问或更改,并且我们需要在某个地方继续存储它。
一旦满足这两个条件,数据就有资格被释放。由于您的情况违反了条件#2——您仍然有打开的文件句柄——数据继续存储在磁盘上(因为它没有其他地方可去),直到文件句柄被关闭。
有些程序甚至使用它来简化数据清理。例如,想象一个程序需要在磁盘上存储一些大数据以进行中间工作,但不需要与其他人共享。如果它打开然后立即删除该文件,它可以使用它,而不必担心确保它们在退出时清理——打开的文件描述符引用计数在close(fd)
或退出时自然会降至 0,并且相关空间将被释放程序是否正常退出。
检测
仍由文件描述符保持打开状态的已删除文件可以通过 找到lsof
,使用如下所示:
% lsof -nP +L1
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NLINK NODE NAME
pulseaudi 1799 cdown 6u REG 0,1 67108864 0 1025 /memfd:pulseaudio (deleted)
chrome 46460 cdown 45r REG 0,27 131072 0 105357 /dev/shm/.com.google.Chrome.gL8tTh (deleted)
这会列出所有st_nlink
值小于 1 的打开文件。
减轻
在您的情况下,您可以通过终止进程来关闭文件句柄,如果可能的话,这是一个很好的解决方案。
如果无法做到这一点,在 Linux 上,您可以通过访问文件描述符支持的数据/proc/pid/fd
并将其截断为大小 0,即使文件已被删除:
: > "/proc/pid/fd/$num"
请注意,根据您的应用程序随后对该文件描述符执行的操作,应用程序可能会对像这样从其下面更改数据感到不同程度的不满。
如果您确定文件描述符只是泄漏并且不会再次访问,那么您也可以使用gdb
关闭它。首先,使用lsof -nP +L1
或ls -l /prod/pid/fd
查找相关的文件描述符编号,然后:
% gdb -p pid --batch -ex 'call close(num)'
回答您的其他问题,尽管这不是您问题的原因:
文件[描述符]的数量是否有限?
文件描述符的数量是有限,但这不是您在这里遇到的限制。 “设备上没有剩余空间”是ENOSPC
,这是当您的文件系统空间不足时我们生成的。如果达到文件描述符限制,您将收到EMFILE
(进程级短缺,呈现strerror
为“打开的文件太多”)或ENFILE
(系统级短缺,呈现strerror
为“系统中打开的文件太多”)。进程级软限制可通过 进行检查ulimit -Sn
,系统级限制可通过 进行查看/proc/sys/fs/file-max
。
答案2
只要您持有(现已删除)文件的句柄,您仍然可以访问其数据(从持有该句柄的进程)。这些数据需要一个地方来存放。