ls -li
这是VFAT 文件系统上命令的输出。
% ls -li
合計 736
1207 drwxr-xr-x 3 root root 16384 3月 10 10:42 efi
1208 -rwxr-xr-x 1 root root 721720 3月 22 14:15 kernel.bin
1207
和1208
是目录和文件的索引节点号。然而,VFAT文件系统没有inode的概念。
Linux 如何为没有 inode 概念的文件系统上的文件分配 inode 号?
答案1
tl;dr:对于虚拟、易失性或与 inode 无关的文件系统,inode 编号通常是在创建 inode 时从单调递增的 32 位计数器生成的。 inode 的其余部分(例如权限)是根据底层文件系统中的等效数据构建的,或者{uid,gid}=
如果不存在这样的概念,则用安装时设置的值(例如)替换。
要回答标题中的问题(即抽象地讲,Linux 如何为没有 inode 概念的文件系统分配 inode 编号),这取决于文件系统。对于某些虚拟或无索引节点文件系统,索引节点号是在实例化时从get_next_ino
池中提取的。但这有很多问题:
get_next_ino()
即使在 64 位内核上也使用 32 位 inode 编号,因为对 32 位用户区的传统处理没有_FILE_OFFSET_BITS=64
;get_next_ino()
只是多个文件系统使用的全局递增计数器,因此溢出的风险进一步增加。
像这样的问题是我的原因之一去年将 tmpfs 从 get_next_ino 支持的 inode 中移开。
因此,tmpfs 是大多数易失性或“无节点”文件系统格式的例外。get_next_ino
从 5.11 开始,套接字、管道、ramfs 等仍然使用该池。
至于您关于 FAT 文件系统的具体问题:fs/fat/inode.c
为 FAT 文件系统分配索引节点号的位置。如果我们往里面看,我们会看到fat_build_inode
(来源):
struct inode *fat_build_inode(struct super_block *sb,
struct msdos_dir_entry *de, loff_t i_pos)
{
struct inode *inode;
int err;
fat_lock_build_inode(MSDOS_SB(sb));
inode = fat_iget(sb, i_pos);
if (inode)
goto out;
inode = new_inode(sb);
if (!inode) {
inode = ERR_PTR(-ENOMEM);
goto out;
}
inode->i_ino = iunique(sb, MSDOS_ROOT_INO);
inode_set_iversion(inode, 1);
err = fat_fill_inode(inode, de);
if (err) {
iput(inode);
inode = ERR_PTR(err);
goto out;
}
fat_attach(inode, i_pos);
insert_inode_hash(inode);
out:
fat_unlock_build_inode(MSDOS_SB(sb));
return inode;
}
这基本上说的是:
- 获取该超级块的 FAT inode 创建锁。
- 检查超级块中该位置是否已存在该索引节点。如果是,则解锁并返回该索引节点。
- 否则,创建一个新的索引节点。
- 获取 inode 号
iunique(sb, MSDOS_ROOT_INO)
(稍后会详细介绍)。 - 从等效的 FAT 数据结构中填充剩余的 inode。
inode->i_ino = iunique(sb, MSDOS_ROOT_INO);
此处设置 inode 编号。iunique
(来源) 是一个与文件系统无关的函数,它为给定的超级块提供唯一的 inode 编号。它通过使用超级块+基于索引节点的哈希表和单调递增的计数器来实现这一点:
ino_t iunique(struct super_block *sb, ino_t max_reserved)
{
static DEFINE_SPINLOCK(iunique_lock);
static unsigned int counter;
ino_t res;
rcu_read_lock();
spin_lock(&iunique_lock);
do {
if (counter <= max_reserved)
counter = max_reserved + 1;
res = counter++;
} while (!test_inode_iunique(sb, res)); /* nb: this checks the hash table */
spin_unlock(&iunique_lock);
rcu_read_unlock();
return res;
}
在这方面,它与前面提到的非常相似get_next_ino
:只是每个超级块而不是全局的(如管道、套接字等),并且具有一些基于基本哈希表的防冲突保护。它甚至继承了get_next_ino
使用 32 位 inode 编号作为尝试避免遗留应用程序上的 EOVERFLOW 的方法的行为,因此可能会有更多的文件系统需要 64 位 inode 修复(就像我前面提到的inode64
tmpfs 实现)未来。
总结一下:
- 大多数虚拟或无 inode 文件系统使用单调递增计数器来表示 inode 编号。
- 即使对于磁盘上无节点文件系统*,该计数器也不稳定。它可能会在重新挂载时更改而无需对文件系统进行其他更改。
- 大多数处于这种状态的文件系统(tmpfs 除外
inode64
)仍在使用 32 位计数器,因此在大量使用的情况下,计数器完全有可能溢出,并且最终可能会出现重复的 inode。
* ...不过,公平地说,根据合同,即使对于以下文件系统也是如此做当变化时有一个 inode 概念i_generation
- 只是在实践中不太可能发生,因为 inode 编号通常与其物理位置或类似位置相关。