https://futures.yunsonbai.top/?hmsr=yunsonbai.top
贵金属行情

Python中关闭文件很重要

https://futures.yunsonbai.top/?hmsr=yunsonbai.top

引言

在使用 Python 操作文件时,很多人会使用上下文管理器来操作。例如:

1
2
with open("xx.txt", mode="w") as f:
f.write("Hello, World!")

该with语句启动上下文管理器。在此示例中,只要上下文处于活动状态,上下文管理器就会打开文件xx.txt并管理文件资源。通常,缩进块结束或引发异常,文件将关闭。可能很多初学者都会被建议这样用,可是为什么要这样写程序才更健壮?或者说为什么一定要关闭文件?

从系统的角度看

系统限制进程打开的文件数

调用open()打开文件时,会对操作系统进行系统调用,硬盘驱动器找到该文件并为读取或写入做好准备。然后,操作系统将返回一个无符号整数n, Python 进程进行系统调用并获取整数 n 作为文件句柄获得与文件关联的编号后,就可以进行读取或写入操作了。
操作系统限制任何单个进程可以拥有的打开文件的数量,这个可以使用工具 ulimit 查看, 或者直接修改 /etc/security/limits.conf 改变文件数限制。

为什么限制?突破限制会怎样

从操作系统的角度来看,任何达到限制的进程都可能会泄漏文件句柄以及其他资源。资源泄漏可能是由于不良的编程习惯或试图攻击系统的恶意程序造成的,操作系统为了保护系统的稳定而采取限制措施。另外,对于大多数应用程序来说,打开这么多文件是没有意义的。在一个硬盘驱动器上最多只能同时进行一次读取或写入操作。

比如你写了一个简单的文件上传服务,在服务中你需要将用户上传的文件保存在服务器上,如果你没有在写完文件后关闭文件,随着服务的时间变长,服务开启的文件会越来越多,最后你会得到这个报错, OSError: [Errno 24] Too many open files。

那能不能突破这个文件限制呢?那肯定是可以的,当然你必须知道你的服务为什么要开启这么多的文件,比如对于一个长连接服务,他可能要求单个实例要支撑数万的tcp链接(Linux中套接字也是文件),那这时候如果还采取默认的单进程开启文件数量限制可能就不合适了,你就需要修正 /etc/security/limits.conf 配置文件。

从稳定性的角度看

我们每次操作文件都去使用with来管理上下文,with封装了 try…except…finally 编码范式,能在任何情况下(当然不包括断电)极大限度的帮你关闭文件。

但是如果我们不关闭文件,将会面临什么?比如下边的代码

1
2
3
4
import os
f = open("xxx.txt", mode="w")
f.write("Hello, world!")
os._exit(1) # 模拟程序异常退出

这段代码你确实可以找到 xxx.txt 文件,但是文件里边没有Hello, world!内容。

为每个写操作都去操作磁盘是很昂贵。出于这个原因,Python 默认是使用一个缓冲区来收集写操作。当缓冲区满时,或者文件被显式关闭时,缓冲区被刷新,写操作完成,这时候才会写入磁盘。

Python 进程完成后,操作系统会执行自己的清理,关闭进程打开的所有文件描述符。崩溃可能发生在多个级别,并会干扰操作系统的清理,使文件句柄悬空。在 Windows 上,悬空文件句柄可能会出现问题,打开文件的进程也会锁定它,另一个进程在关闭之前无法打开该文件。Windows 用户可能熟悉不允许您打开或删除文件的恶意进程。文件句柄泄漏和缓冲区中的内容丢失已经够糟糕的了,但是中断文件操作的崩溃也可能导致文件损坏。

总结

首先因为受系统的限制,我们不管用哪种语言编程,都要把不在使用的文件描述符关闭,一来避免因开启文件过多导致服务异常,二来及时关闭不用的文件描述符也能节约资源。

另外我们永远无法完全避免崩溃的影响,但是我们可以通过使用上下文管理器来减少崩溃带来的影响。

yunsonbai wechat
公众号:技术and生活