理解Linux中的陈旧文件句柄
概述
在本文中,我们将讨论陈旧文件句柄的概念以及如何在应用程序中避免它。我们还将看到一些关于如何使用fcntl()函数检查文件句柄是否有效的例子。
本教程中的代码已在带有GNU Bash 5.0.3的Debian 10.10 (Buster)上进行了测试。它是POSIX兼容的,应该在任何此类环境中都能工作。
什么是陈旧文件句柄?
文件句柄可以被认为是一个整数,它代表特定文件的访问权限。文件系统维护所有打开的文件及其相应文件句柄的列表。当使用“close”命令关闭文件时,操作系统会自动将其引用从其内部数据结构中删除,并相应地更新文件描述符表(FDT)。这意味着当您尝试打开另一个同名的文件时,操作系统会检查是否与该文件名关联的任何现有文件句柄。如果是,则将现有文件句柄返回给您。否则,它将创建一个新的文件句柄并将其分配给新打开的文件。
索引节点
索引节点(inode)是文件系统元素,它描述其他文件。基本上,在创建任何文件或目录之类的对象之前,必须存在inode。任何具有关联inode的文件系统对象都称为“文件系统对象”。
我们使用stats()函数显示特定文本文档的详细信息。
user@baeldung:~$ stat filename File: filename Size: 583 Blocks: 8 IO Block: 4096 regular file Device: 810h/2064d Inode: 666 Links: 1 [...]
文件名引用索引节点号666(最后一行)。
谁引用了该文件?
文件句柄
文件句柄(文件描述符)只是数字。它们用于为打开的文件描述符的索引系统表存储文件描述。与文件系统元素不同,文件句柄不会驻留在文件系统上或随文件系统更新。相反,它们被进程用来跟踪其打开的文件。
我们使用ls(列表)命令和proc文件系统上的−l(长列表格式)标志列出此文件−
user@baeldung:~$ ls -l /proc/1296/fd total 0 lrwx------ 1 user user 64 Jul 1 10:49 0 -> /dev/pts/1 lrwx------ 1 user user 64 Jul 1 10:49 1 -> /dev/pts/1 lrwx------ 1 user user 64 Jul 1 10:49 2 -> /dev/pts/1 lrwx------ 1 user user 64 Jul 1 10:50 3 -> /home/user/filename
输出表明fd3指向/home/user/filename。在fd3上使用取消引用命令(-L)告诉我们该路径的inode编号为2。
user@baeldung:~$ stat -L /proc/1296/fd/3 File: /proc/1296/fd/3 Size: 583 Blocks: 8 IO Block: 4096 regular file Device: 810h/2064d Inode: 666 Links: 1 [...]
进程可以使用其名称(例如,/tmp/foo)引用特定的inode,但是如果inode已被重命名,则进程将无法访问它。
陈旧文件句柄
一旦inode不再被引用或链接,它就可供将来使用。
在此阶段,相关文件的打开句柄指向无效的inode编号或与之前的不同。系统几乎不可能再次重用相同的inode编号。
不成功上传的结果以不同的方式出现,但一种常见的方式是ESTALE 116,并显示消息“陈旧文件句柄”。
user@baeldung:~$ cd /mounted -bash: cd: /mounted: Stale file handle
如果进程没有成功完成,它可能只是静默失败,而不会产生任何输出或声称缺少文件。
这个问题通常只有一个标准解决方案。
解决陈旧文件句柄
当进程打开陈旧句柄时,它会得到一个新的句柄。如果文件已经有一个inode,则更新其inode将更新其文件描述。大多数情况下,该过程必须在内部完成。如果不是,我们可能必须重新开始。
使用挂载共享目录时,此解决方案可能并非易事。
使用NFS或CIFs时,陈旧文件未刷新的最常见场景是当它们用于挂载共享文件夹时。协议实现通常缺乏标准化的缓存和文件句柄协调。此类场景通常需要重新挂载(可能使用不同的设置)甚至重新启动服务器进程。这两个操作都可能导致某些停机时间。
总结
我们查看了特定软件用于访问文件的引用链。我们看到了该链如何断裂。
我们展示了如何在发生陈旧文件句柄的不同情况下修复它们。