问题描述
该程序是一个标准的烧瓶程序,它会在初始化过程中进行一些清理工作。在使用 os.remove("abc.txt")
的 cleanup() 过程中,我注意到该文件已被删除,但并未被操作系统回收。
我同时使用“python website.py”和“gunicorn website:app”来运行该应用程序,并且在 Linux 环境中都存在相同的问题。在 MacOS 中,我无法重现它。
文件是 os.remove 后,它不再列在“ls”命令中,而是当我运行时
lsof | grep deleted
我仍然可以看到这个文件被列为已删除但被 python 应用程序打开。
因为这个文件已经是“os.remove”,所以没有列在ls
命令中,du
不会计算这个文件。
但是如果这个文件足够大,df
命令会显示这个文件的空间还在被占用,没有被回收。因为这个文件仍然“被烧瓶应用程序打开”,正如 lsof
程序声称的那样。
一旦我停止运行 Flask 应用程序,lsof
将没有此文件,并且空间将被回收。
通常当文件太小,或者应用程序频繁停止或重新启动时,您不会注意到空间被占用。但这对于保持空间来说并不是很合理。我希望该网站可以运行多年。
在 Internet 上搜索“打开但已删除的文件”时,大多数建议是“找到该应用程序并杀死它”。有没有办法让 Flask 应用程序保持运行而不重新启动它?我的应用程序实际上并没有“打开”这个文件,只是简单地 os.remove 它。
解决方法
Flask 应用程序要么需要大文件继续运行,要么不释放不需要的资源。 如果应用程序需要大文件,就是这样。否则,应用程序有问题,需要更正。 在这两种情况下,您的脚本都无法控制大文件的“正在打开”状态(至少在 Linux 上,这会导致文件仍然存在于大容量内存系统中)。
,os.remove()
仅将文件的删除委托给操作系统。如果您的代码中仍然引用了该文件,那么 lsof
将显示该文件,当然。如果不提供代码,就很难判断不需要的行为来自哪里。但至少我可以给你一些关于引用行为的见解。
这是一个小脚本,它应该只向您显示如果文件被引用,它仍然可以被认为是打开的。
import os
import psutil
PATH = "abc.txt"
def write_file(filepath):
"""Simulating existing file with correctly closing it at the end"""
with open(filepath,"x") as file:
file.write("Hello,world!")
def remove_file(filepath):
"""Let the operating system handle the file removement"""
os.remove(filepath)
def lsof():
"""Simulating lsof command (requires e.g. `pip install psutil`)"""
p = psutil.Process()
open_files = p.open_files()
if open_files:
return "\n".join(os.path.basename(p.path) for p in p.open_files())
else:
return "No open files found."
if __name__ == "__main__":
print("\n----- EXAMPLE 1 -----\n")
write_file(PATH)
print(lsof())
remove_file(PATH)
print(lsof())
print("\n----- EXAMPLE 2 -----\n")
write_file(PATH)
file = open(PATH) # referenced!
print(lsof())
remove_file(PATH)
print(lsof())
示例 2 的输出显示,在引用文件后,它也可用于 lsof
命令:
----- EXAMPLE 1 -----
No open files found.
No open files found.
----- EXAMPLE 2 -----
abc.txt
No open files found.
这两个示例还向您展示了删除文件后不再有打开的文件描述符。
您可以尝试调试您的代码,例如使用 psutil.Process.open_files()
类似于我的示例,以找出应该关闭特定文件的期望值不匹配的地方。