跳至主要內容

关于 RT-Thread DFS 中 CLOSE 失败后处理方式的一些看法

WKJay...大约 4 分钟开发笔记RT-ThreadDFS虚拟文件系统

关于 RT-Thread DFS 中 CLOSE 失败后处理方式的一些看法

前两天项目中出现一个情况:

运行过程中需要通过文件记录一些数据,这些文件是存在SD卡中的,而SD卡会被拔出然后再插入。然而再次插入后发现读不出数据了。经过排查发现是由于需要记录数据,设备开机会打开文件,并且保持打开的状态,一旦检测到SD卡被拔出会自动关闭文件。但是由于SD卡已经被拔出,所以关闭文件是必然失效的。所以导致读不出数据。

针对上述问题,第一个想到的方案就是文件系统被重新挂载(SD卡重新插入)的时候再次打开一次文件,这也是可行的,但是先前打开的 fd 呢,是否在调用 close 的时候就会被释放?

很不幸,当 close 失败时,DFS 并不会释放这个 fd 及其所占用的资源。并且由于在上述情况下 close 永远不会成功,所以这部分资源永远不会被释放。

这一点是很致命的,因为无论你做什么操作都无法释放这一部分的内存,即使通过新打开一个文件的方式可以避免无法读取数据,但是随着热插拔的次数增多,泄漏的 fd 也会增加,直到达到上限无法再打开新的文件,或者在这之前内存已经到达上限导致系统无法正常运行。

当然出现上述问题的根本原因是 close 失败了,因此我们应该思考当 close 失败的时候,分配的资源到底要不要释放。

在我遇到的情况中,close 失败后我除了记录一个错误或提示出来之外没有任何办法去做实际的处理,因为这个文件描述符无法再恢复,即使拔了SD卡再插入,也是无效的描述符,并不会被文件系统识别。此时我能做的也就是抛弃原来的文件描述符,并且重新打开。反应到程序里也就是我希望 close 不管能不能成功,都要释放资源,因为即使不成功,我也没办法再次让他成功。或者还有另一种方式,就是提供一个 force close 的接口,能够强制关闭并释放资源。但实际好像并没有类似的接口。

在查阅了关于 close 的一些资料后发现如下一段文字:

A careful programmer will check the return value of close(),
since it is quite possible that errors on a previous write(2)
operation are reported only on the final close() that releases
the open file description. Failing to check the return value
when closing a file may lead to silent loss of data. This can
especially be observed with NFS and with disk quota.

Note, however, that a failure return should be used only for
diagnostic purposes (i.e., a warning to the application that
there may still be I/O pending or there may have been failed I/O)
or remedial purposes (e.g., writing the file once more or
creating a backup).

Retrying the close() after a failure return is the wrong thing to
do, since this may cause a reused file descriptor from another
thread to be closed. This can occur because the Linux kernel
always releases the file descriptor early in the close operation,
freeing it for reuse; the steps that may return an error, such as
flushing data to the filesystem or device, occur only later in the close operation.

摘自:https://man7.org/linux/man-pages/man2/close.2.htmlopen in new window

由上述文字的最后一段可以看到在 Linux 内核中 close 是不会根据操作是否成功来决定是否要释放资源的,只要调用 close 就会对文件描述符的资源进行释放。该操作和我设想的是一致的。

为了再次印证我的猜想,我查阅了 Linux 的内核代码,最终在 open.c 中发现:

int filp_close(struct file *filp, fl_owner_t id)
{
	int retval = 0;

	if (CHECK_DATA_CORRUPTION(file_count(filp) == 0,
			"VFS: Close: file count is 0 (f_op=%ps)",
			filp->f_op)) {
		return 0;
	}

	if (filp->f_op->flush)
		retval = filp->f_op->flush(filp, id);

	if (likely(!(filp->f_mode & FMODE_PATH))) {
		dnotify_flush(filp, id);
		locks_remove_posix(filp, id);
	}
	fput(filp);
	return retval;
}

可以看到这里面不论 flush 成功或者失败,filp 都会被释放。也进一步证实了在 Linux 中 close 是通过强制释放资源来避免产生资源的泄漏的。

综上,在RTT中我认为也可以使用这种方式,无论 close 成功与否,都要释放资源,毕竟一旦失败,我也没有任何办法能使其恢复。

相关PR:
https://github.com/RT-Thread/rt-thread/pull/7907open in new window

上次编辑于:
贡献者: WKJay
评论
  • 按正序
  • 按倒序
  • 按热度
Powered by Waline v2.15.5