[toc]
fs/inode.c Inode管理 VFS中文件对象的抽象核心 历史与背景 这项技术是为了解决什么特定问题而诞生的? 这项技术以及其核心数据结构struct inode
,是为了在Linux内核中实现一个**统一的文件系统接口(VFS)**而诞生的。它解决了在支持多种不同文件系统时面临的根本性挑战:
抽象与统一 :不同的文件系统(如ext4, XFS, FAT, NFS)在磁盘上存储文件元数据(metadata)的方式千差万别。例如,ext4的磁盘inode结构与XFS的完全不同。为了让内核的上层代码(如系统调用实现)能够以一种统一的方式操作任何文件,而无需关心其底层文件系统类型,VFS需要一个通用的、在内存中的文件对象表示。struct inode
就是这个抽象。
性能(元数据缓存) :访问磁盘是极其缓慢的。如果每次需要读取文件元数据(如文件大小、权限、所有者等)时都必须从磁盘读取,系统性能将非常低下。fs/inode.c
管理着一个全局的inode缓存 ,将最近使用的inode对象保存在内存中,极大地加速了对文件元数据的访问。
状态管理 :一个文件在内存中有许多运行时的状态,这些状态在磁盘上是不存在的。例如,需要跟踪inode是否被修改过(“脏”状态,I_DIRTY
)、inode是否被锁定、有多少个内核组件正在引用它等。fs/inode.c
负责管理struct inode
的这些内存中的状态。
它的发展经历了哪些重要的里程碑或版本迭代? inode
的概念继承自早期的UNIX系统,而Linux中的inode
管理实现则随着内核的演进而不断优化。
从全局数组到哈希缓存 :早期的内核可能使用简单的数组或链表来管理inode。现代内核则使用一个高效的、可扩展的哈希表来缓存和快速查找inode。
与Slab分配器的集成 :为了高效地分配和释放大量的struct inode
对象,inode管理与内核的Slab分配器紧密集成。每个文件系统都可以创建自己的inode缓存池(kmem_cache
)。
锁机制的演进 :随着多核(SMP)系统的普及,对inode的并发访问成为性能瓶yll颈。inode的锁机制经历了从单一全局锁到更细粒度的锁(如每个inode内部的自旋锁i_lock
和互斥锁i_mutex
)的演进,以提高并行性。
脏inode的回写机制 :inode
与页面回写(page-writeback)机制紧密集成。fs/inode.c
负责管理一个脏inode列表,flusher线程会定期将这些脏inode的元数据写回到磁盘,以确保数据持久化。
目前该技术的社区活跃度和主流应用情况如何? inode
管理是VFS乃至整个Linux内核的基石,其重要性无可替代。
社区活跃度 :fs/inode.c
的代码是内核中最稳定、最核心的部分之一。相关的改动非常谨慎,通常是为了修复深层次的竞态条件、进行性能微调,或与内存管理、块设备层的新特性进行集成。
主流应用 :任何与文件系统相关的操作都离不开inode
。它是所有文件系统在VFS层面的统一代表。从打开文件(open
)到读取属性(stat
),再到修改权限(chmod
),每一个操作都涉及在内核中查找、操作并最终释放对一个struct inode
对象的引用。
核心原理与设计 它的核心工作原理是什么? fs/inode.c
的核心是管理struct inode
对象的生命周期和缓存。
VFS Inode的定义 :struct inode
是一个通用的内核数据结构,包含了所有文件系统都必须提供的标准信息,如:
i_mode
:文件类型(普通文件、目录、链接等)和权限。
i_uid
, i_gid
:所有者和所属组。
i_size
:文件大小(以字节为单位)。
i_atime
, i_mtime
, i_ctime
:访问、修改和状态改变时间。
i_nlink
:硬链接计数 ,即有多少个文件名指向这个inode。这是磁盘上的持久计数值。
i_count
:引用计数 ,即在内存中有多少个内核组件正在使用这个struct inode
对象。这是一个纯粹的内存状态。
i_op
, i_fop
:指向操作函数表的指针(inode_operations
, file_operations
),这是实现多态性、调用具体文件系统代码的关键。
i_mapping
:指向address_space
结构,将inode与它在Page Cache中的数据页关联起来。
Inode缓存 :内核维护一个全局的inode哈希表。当需要访问一个文件时(例如,在dcache中查找到一个dentry后),内核会通过iget(superblock, inode_number)
来获取对应的inode。iget
会首先在哈希表中查找。如果找到,就增加其引用计数i_count
并返回;如果没找到,它会调用文件系统的read_inode
回调函数,从磁盘读取元数据,在内存中“填充”一个新的struct inode
对象,并将其加入缓存。
生命周期管理 :
获取 :每次需要使用inode时,都会增加i_count
。
释放 :当一个组件使用完毕,它会调用iput(inode)
来减少i_count
。
销毁 :当i_count
减到0时,意味着内存中不再有任何地方引用这个inode。此时,如果inode的硬链接数i_nlink
也为0(意味着文件已被删除),内核就会将该inode标记为可回收,并最终调用文件系统的evict_inode
回调来清理它,释放其占用的数据块。
与具体文件系统的交互 :VFS inode
通过函数指针与底层文件系统交互。当上层代码对一个inode
执行lookup
、create
等操作时,VFS会通过inode->i_op->lookup(...)
调用到具体文件系统(如ext4)提供的实现函数。
它的主要优势体现在哪些方面?
抽象和解耦 :为内核提供了一个稳定、统一的接口来操作文件,将通用逻辑与文件系统特定逻辑完全分离开。
性能 :通过缓存inode,避免了频繁的、昂贵的磁盘元数据读取操作。
集中管理 :提供了集中的生命周期、状态和锁管理,简化了文件系统的编写,并提高了整个系统的健壮性。
它存在哪些已知的劣势、局限性或在特定场景下的不适用性?
内存消耗 :与dcache类似,在一个拥有海量文件的系统上,inode缓存也会消耗大量的内核内存。
复杂性 :inode的生命周期、状态转换以及与dcache、Page Cache和回写机制的复杂交互,使得这部分代码成为内核中最难理解和调试的部分之一。
使用场景 在哪些具体的业务或技术场景下,它是首选解决方案?请举例说明。 与dcache一样,inode不是一个可选方案,而是所有基于VFS的文件操作的强制性核心组件 。
stat
系统调用 :当用户执行ls -l
或stat file
时,内核找到文件的inode后,直接从内存中的struct inode
对象读取文件大小、权限、所有者等信息并返回给用户,通常无需任何磁盘I/O。
chmod
系统调用 :执行chmod 755 file
时,内核找到inode,修改其i_mode
字段,然后将该inode标记为“脏”。这个修改会由flusher线程在稍后异步地写回磁盘。
硬链接 :执行ln file1 file2
时,内核找到file1
的inode,然后创建一个新的dentry(file2
),将其指向同一个inode,并原子地将该inode的i_nlink
计数加1。
是否有不推荐使用该技术的场景?为什么? 不存在。任何希望被Linux系统识别和操作的文件系统,都必须 实现与VFS inode
模型的集成。绕过VFS和inode直接操作块设备的应用(如某些高性能数据库),实际上是在用户空间重新实现了一套自己的、功能类似的文件和元数据管理系统。
对比分析 请将其 与 其他相似技术 进行详细对比。 对比一:inode
vs. dentry
(fs/dcache.c
)
这是VFS中最重要的一对概念,理解它们的区别是理解VFS的关键。
特性
inode (索引节点)
dentry (目录项)
代表对象
代表一个文件实体 或文件对象 。是文件的元数据集合。
代表一个路径组件 或文件名 。是连接路径和文件实体的“路标”。
包含信息
文件的属性:大小、权限、所有者、时间戳、数据块指针等。
文件的名称、指向父dentry的指针、指向inode的指针。
关系
一对多 。一个inode可以被多个dentry引用(硬链接的情况)。
多对一 。多个dentry可以指向同一个inode。
生命周期
只要i_nlink > 0
(磁盘上存在)或i_count > 0
(内存中被引用),inode就有效。
dentry是纯粹的内存缓存对象,即使对应的文件存在,其dentry也可能因内存压力被回收。
存在性
与磁盘上的物理实体一一对应。
可以是“负dentry”,即缓存一个不存在的文件名查询结果。
对比二:VFS inode
(struct inode
) vs. On-Disk Inode (如 struct ext4_inode
)
特性
VFS Inode (struct inode
)
On-Disk Inode (e.g., struct ext4_inode
)
存在位置
内存中 。
持久化存储介质上(磁盘、SSD) 。
结构
通用、庞大 。包含了所有文件系统通用的字段,以及大量运行时状态(如锁、链表指针、引用计数)。
特定、紧凑 。只包含需要持久化存储的元数据,其布局由具体文件系统定义。
生命周期
动态创建和销毁,作为磁盘inode在内存中的一个缓存实例 。
持久存在,直到文件被删除且其空间被重用。
作用
为内核上层提供统一的操作接口 。
持久地记录 文件的元数据。
关系
VFS Inode是On-Disk Inode在内存中的运行时表示 。文件系统的read_inode
函数负责从磁盘读取On-Disk Inode并用其信息填充VFS Inode。
include/linux/fs.h inode_wake_up_bit 唤醒等待 inode(文件系统中的索引节点)特定状态位(bit)的线程 1 2 3 4 5 static inline void inode_wake_up_bit (struct inode *inode, u32 bit) { wake_up_var(inode_state_wait_address(inode, bit)); }
alloc_inode_sb 用于分配 inode(文件系统中的索引节点) 1 2 3 4 #define alloc_inode_sb(_sb, _cache, _gfp) kmem_cache_alloc_lru(_cache, &_sb->s_inode_lru, _gfp)
inode_init_always 用于初始化 inode(文件系统中的索引节点) 1 2 3 4 static inline int inode_init_always (struct super_block *sb, struct inode *inode) { return inode_init_always_gfp(sb, inode, GFP_NOFS); }
inode_fsuid_set 使用调用者的 fsuid 初始化 inode 的 i_uid 字段 1 2 3 4 5 6 7 8 9 10 11 12 static inline void inode_fsuid_set (struct inode *inode, struct mnt_idmap *idmap) { inode->i_uid = mapped_fsuid(idmap, i_user_ns(inode)); }
fs/proc/inode.c proc_sys_invalidate_dcache 清除 proc 的目录缓存 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 void proc_invalidate_siblings_dcache (struct hlist_head *inodes, spinlock_t *lock) { struct hlist_node *node ; struct super_block *old_sb = NULL ; rcu_read_lock(); while ((node = hlist_first_rcu(inodes))) { struct proc_inode *ei = hlist_entry(node, struct proc_inode, sibling_inodes); struct super_block *sb ; struct inode *inode ; spin_lock(lock); hlist_del_init_rcu(&ei->sibling_inodes); spin_unlock(lock); inode = &ei->vfs_inode; sb = inode->i_sb; if ((sb != old_sb) && !atomic_inc_not_zero(&sb->s_active)) continue ; inode = igrab(inode); rcu_read_unlock(); if (sb != old_sb) { if (old_sb) deactivate_super(old_sb); old_sb = sb; } if (unlikely(!inode)) { rcu_read_lock(); continue ; } if (S_ISDIR(inode->i_mode)) { struct dentry *dir = d_find_any_alias(inode); if (dir) { d_invalidate(dir); dput(dir); } } else { struct dentry *dentry; while ((dentry = d_find_alias(inode))) { d_invalidate(dentry); dput(dentry); } } iput(inode); rcu_read_lock(); } rcu_read_unlock(); if (old_sb) deactivate_super(old_sb); } static void proc_sys_invalidate_dcache (struct ctl_table_header *head) { proc_invalidate_siblings_dcache(&head->inodes, &sysctl_lock); }
fs/inode.c inode_init_early 初始化 inode 的早期阶段 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 void __init inode_init_early (void ) { if (hashdist) return ; inode_hashtable = alloc_large_system_hash("Inode-cache" , sizeof (struct hlist_head), ihash_entries, 14 , HASH_EARLY | HASH_ZERO, &i_hash_shift, &i_hash_mask, 0 , 0 ); }
__address_space_init_once 用于初始化地址空间(address space)的一次性设置 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 static void __address_space_init_once(struct address_space *mapping){ xa_init_flags(&mapping->i_pages, XA_FLAGS_LOCK_IRQ | XA_FLAGS_ACCOUNT); init_rwsem(&mapping->i_mmap_rwsem); INIT_LIST_HEAD(&mapping->i_private_list); spin_lock_init(&mapping->i_private_lock); mapping->i_mmap = RB_ROOT_CACHED; }
init_once 用于初始化 inode 的一次性设置 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 void inode_init_once (struct inode *inode) { memset (inode, 0 , sizeof (*inode)); INIT_HLIST_NODE(&inode->i_hash); INIT_LIST_HEAD(&inode->i_devices); INIT_LIST_HEAD(&inode->i_io_list); INIT_LIST_HEAD(&inode->i_wb_list); INIT_LIST_HEAD(&inode->i_lru); INIT_LIST_HEAD(&inode->i_sb_list); __address_space_init_once(&inode->i_data); i_size_ordered_init(inode); } EXPORT_SYMBOL(inode_init_once); static void init_once (void *foo) { struct inode *inode = (struct inode *) foo; inode_init_once(inode); }
inode_init 设置与索引节点(inode)相关的缓存和哈希表 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 void __init inode_init (void ) { inode_cachep = kmem_cache_create("inode_cache" , sizeof (struct inode), 0 , (SLAB_RECLAIM_ACCOUNT|SLAB_PANIC| SLAB_ACCOUNT), init_once); if (!hashdist) return ; inode_hashtable = alloc_large_system_hash("Inode-cache" , sizeof (struct hlist_head), ihash_entries, 14 , HASH_ZERO, &i_hash_shift, &i_hash_mask, 0 , 0 ); }
__iget 用于增加 inode 的引用计数 1 2 3 4 5 6 7 static inline void __iget(struct inode *inode){ atomic_inc (&inode->i_count); }
igrab 用于增加 inode 的引用计数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 struct inode *igrab (struct inode *inode) { spin_lock(&inode->i_lock); if (!(inode->i_state & (I_FREEING|I_WILL_FREE))) { __iget(inode); spin_unlock(&inode->i_lock); } else { spin_unlock(&inode->i_lock); inode = NULL ; } return inode; } EXPORT_SYMBOL(igrab);
__inode_add_lru 用于将 inode 添加到 LRU(最近最少使用)列表中
LRU 缓存是一种常见的资源管理机制,用于优先保留最近使用的对象并逐步移除较少使用的对象。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 static void __inode_add_lru(struct inode *inode, bool rotate){ if (inode->i_state & (I_DIRTY_ALL | I_SYNC | I_FREEING | I_WILL_FREE)) return ; if (atomic_read (&inode->i_count)) return ; if (!(inode->i_sb->s_flags & SB_ACTIVE)) return ; if (!mapping_shrinkable(&inode->i_data)) return ; if (list_lru_add_obj(&inode->i_sb->s_inode_lru, &inode->i_lru)) this_cpu_inc(nr_unused); else if (rotate) inode->i_state |= I_REFERENCED; }
inode_wait_for_lru_isolating 等待 inode(文件系统中的索引节点)完成 LRU(最近最少使用)隔离操作 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 static void inode_wait_for_lru_isolating (struct inode *inode) { struct wait_bit_queue_entry wqe ; struct wait_queue_head *wq_head ; lockdep_assert_held(&inode->i_lock); if (!(inode->i_state & I_LRU_ISOLATING)) return ; wq_head = inode_bit_waitqueue(&wqe, inode, __I_LRU_ISOLATING); for (;;) { prepare_to_wait_event(wq_head, &wqe.wq_entry, TASK_UNINTERRUPTIBLE); if (!(inode->i_state & I_LRU_ISOLATING)) break ; spin_unlock(&inode->i_lock); schedule(); spin_lock(&inode->i_lock); } finish_wait(wq_head, &wqe.wq_entry); WARN_ON(inode->i_state & I_LRU_ISOLATING); }
destroy_inode 用于销毁 inode(文件系统中的索引节点) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 void free_inode_nonrcu (struct inode *inode) { kmem_cache_free(inode_cachep, inode); } EXPORT_SYMBOL(free_inode_nonrcu); static void i_callback (struct rcu_head *head) { struct inode *inode = container_of(head, struct inode, i_rcu); if (inode->free_inode) inode->free_inode(inode); else free_inode_nonrcu(inode); } void __destroy_inode(struct inode *inode){ BUG_ON(inode_has_buffers(inode)); inode_detach_wb(inode); security_inode_free(inode); fsnotify_inode_delete(inode); locks_free_lock_context(inode); if (!inode->i_nlink) { WARN_ON(atomic_long_read(&inode->i_sb->s_remove_count) == 0 ); atomic_long_dec(&inode->i_sb->s_remove_count); } #ifdef CONFIG_FS_POSIX_ACL if (inode->i_acl && !is_uncached_acl(inode->i_acl)) posix_acl_release(inode->i_acl); if (inode->i_default_acl && !is_uncached_acl(inode->i_default_acl)) posix_acl_release(inode->i_default_acl); #endif this_cpu_dec(nr_inodes); } EXPORT_SYMBOL(__destroy_inode); static void destroy_inode (struct inode *inode) { const struct super_operations *ops = inode->i_sb->s_op; BUG_ON(!list_empty(&inode->i_lru)); __destroy_inode(inode); if (ops->destroy_inode) { ops->destroy_inode(inode); if (!ops->free_inode) return ; } inode->free_inode = ops->free_inode; call_rcu(&inode->i_rcu, i_callback); }
evict 释放传入的 inode(文件系统中的索引节点),并从其关联的所有列表中移除 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 static void evict (struct inode *inode) { const struct super_operations *op = inode->i_sb->s_op; BUG_ON(!(inode->i_state & I_FREEING)); BUG_ON(!list_empty(&inode->i_lru)); if (!list_empty(&inode->i_io_list)) inode_io_list_del(inode); inode_sb_list_del(inode); spin_lock(&inode->i_lock); inode_wait_for_lru_isolating(inode); inode_wait_for_writeback(inode); spin_unlock(&inode->i_lock); if (op->evict_inode) { op->evict_inode(inode); } else { truncate_inode_pages_final(&inode->i_data); clear_inode(inode); } if (S_ISCHR(inode->i_mode) && inode->i_cdev) cd_forget(inode); remove_inode_hash(inode); inode_wake_up_bit(inode, __I_NEW); BUG_ON(inode->i_state != (I_FREEING | I_CLEAR)); destroy_inode(inode); }
iput_final 用于处理文件系统中 inode 的最后一个引用被释放的情况 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 static inline int generic_drop_inode (struct inode *inode) { return !inode->i_nlink || inode_unhashed(inode); } static void iput_final (struct inode *inode) { struct super_block *sb = inode->i_sb; const struct super_operations *op = inode->i_sb->s_op; unsigned long state; int drop; WARN_ON(inode->i_state & I_NEW); if (op->drop_inode) drop = op->drop_inode(inode); else drop = generic_drop_inode(inode); if (!drop && !(inode->i_state & I_DONTCACHE) && (sb->s_flags & SB_ACTIVE)) { __inode_add_lru(inode, true ); spin_unlock(&inode->i_lock); return ; } state = inode->i_state; if (!drop) { WRITE_ONCE(inode->i_state, state | I_WILL_FREE); spin_unlock(&inode->i_lock); write_inode_now(inode, 1 ); spin_lock(&inode->i_lock); state = inode->i_state; WARN_ON(state & I_NEW); state &= ~I_WILL_FREE; } WRITE_ONCE(inode->i_state, state | I_FREEING); if (!list_empty(&inode->i_lru)) inode_lru_list_del(inode); spin_unlock(&inode->i_lock); evict(inode); }
iput 用于减少 inode 的引用计数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 void iput (struct inode *inode) { if (!inode) return ; BUG_ON(inode->i_state & I_CLEAR); retry: if (atomic_dec_and_lock(&inode->i_count, &inode->i_lock)) { if (inode->i_nlink && (inode->i_state & I_DIRTY_TIME)) { atomic_inc (&inode->i_count); spin_unlock(&inode->i_lock); trace_writeback_lazytime_iput(inode); mark_inode_dirty_sync(inode); goto retry; } iput_final(inode); } } EXPORT_SYMBOL(iput);
inode_bit_waitqueue 用于获取 inode 的等待队列头 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 #define inode_state_wait_address(inode, bit) ((char *)&(inode)->i_state + (bit)) struct wait_queue_head *inode_bit_waitqueue (struct wait_bit_queue_entry *wqe, struct inode *inode, u32 bit) { void *bit_address; bit_address = inode_state_wait_address(inode, bit); init_wait_var_entry(wqe, bit_address, 0 ); return __var_waitqueue(bit_address); } EXPORT_SYMBOL(inode_bit_waitqueue);
alloc_inode 获取一个 inode 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 struct inode *alloc_inode (struct super_block *sb) { const struct super_operations *ops = sb->s_op; struct inode *inode ; if (ops->alloc_inode) inode = ops->alloc_inode(sb); else inode = alloc_inode_sb(sb, inode_cachep, GFP_KERNEL); if (!inode) return NULL ; if (unlikely(inode_init_always(sb, inode))) { if (ops->destroy_inode) { ops->destroy_inode(inode); if (!ops->free_inode) return NULL ; } inode->free_inode = ops->free_inode; i_callback(&inode->i_rcu); return NULL ; } return inode; }
inode_sb_list_add 将 inode 添加到超级块的 inode 列表中 1 2 3 4 5 6 7 8 9 10 11 12 13 void inode_sb_list_add (struct inode *inode) { struct super_block *sb = inode->i_sb; spin_lock(&sb->s_inode_list_lock); list_add(&inode->i_sb_list, &sb->s_inodes); spin_unlock(&sb->s_inode_list_lock); } EXPORT_SYMBOL_GPL(inode_sb_list_add);
new_inode 获取一个inode 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 struct inode *new_inode (struct super_block *sb) { struct inode *inode ; inode = alloc_inode(sb); if (inode) inode_sb_list_add(inode); return inode; } EXPORT_SYMBOL(new_inode);
inode_init_always_gfp 用于执行 inode 结构的初始化 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 int inode_init_always_gfp (struct super_block *sb, struct inode *inode, gfp_t gfp) { static const struct inode_operations empty_iops ; static const struct file_operations no_open_fops = {.open = no_open}; struct address_space *const mapping = &inode->i_data; inode->i_sb = sb; inode->i_blkbits = sb->s_blocksize_bits; inode->i_flags = 0 ; inode->i_state = 0 ; atomic64_set(&inode->i_sequence, 0 ); atomic_set (&inode->i_count, 1 ); inode->i_op = &empty_iops; inode->i_fop = &no_open_fops; inode->i_ino = 0 ; inode->__i_nlink = 1 ; inode->i_opflags = 0 ; if (sb->s_xattr) inode->i_opflags |= IOP_XATTR; if (sb->s_type->fs_flags & FS_MGTIME) inode->i_opflags |= IOP_MGTIME; i_uid_write(inode, 0 ); i_gid_write(inode, 0 ); atomic_set (&inode->i_writecount, 0 ); inode->i_size = 0 ; inode->i_write_hint = WRITE_LIFE_NOT_SET; inode->i_blocks = 0 ; inode->i_bytes = 0 ; inode->i_generation = 0 ; inode->i_pipe = NULL ; inode->i_cdev = NULL ; inode->i_link = NULL ; inode->i_dir_seq = 0 ; inode->i_rdev = 0 ; inode->dirtied_when = 0 ; #ifdef CONFIG_CGROUP_WRITEBACK inode->i_wb_frn_winner = 0 ; inode->i_wb_frn_avg_time = 0 ; inode->i_wb_frn_history = 0 ; #endif spin_lock_init(&inode->i_lock); lockdep_set_class(&inode->i_lock, &sb->s_type->i_lock_key); init_rwsem(&inode->i_rwsem); lockdep_set_class(&inode->i_rwsem, &sb->s_type->i_mutex_key); atomic_set (&inode->i_dio_count, 0 ); mapping->a_ops = &empty_aops; mapping->host = inode; mapping->flags = 0 ; mapping->wb_err = 0 ; atomic_set (&mapping->i_mmap_writable, 0 ); #ifdef CONFIG_READ_ONLY_THP_FOR_FS atomic_set (&mapping->nr_thps, 0 ); #endif mapping_set_gfp_mask(mapping, GFP_HIGHUSER_MOVABLE); mapping->i_private_data = NULL ; mapping->writeback_index = 0 ; init_rwsem(&mapping->invalidate_lock); lockdep_set_class_and_name(&mapping->invalidate_lock, &sb->s_type->invalidate_lock_key, "mapping.invalidate_lock" ); if (sb->s_iflags & SB_I_STABLE_WRITES) mapping_set_stable_writes(mapping); inode->i_private = NULL ; inode->i_mapping = mapping; INIT_HLIST_HEAD(&inode->i_dentry); #ifdef CONFIG_FS_POSIX_ACL inode->i_acl = inode->i_default_acl = ACL_NOT_CACHED; #endif #ifdef CONFIG_FSNOTIFY inode->i_fsnotify_mask = 0 ; #endif inode->i_flctx = NULL ; if (unlikely(security_inode_alloc(inode, gfp))) return -ENOMEM; this_cpu_inc(nr_inodes); return 0 ; } EXPORT_SYMBOL(inode_init_always_gfp);
get_next_ino 获生成文件系统的下一个 inode 编号(inode number) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 #define LAST_INO_BATCH 1024 static DEFINE_PER_CPU (unsigned int , last_ino) ;unsigned int get_next_ino (void ) { unsigned int *p = &get_cpu_var(last_ino); unsigned int res = *p; #ifdef CONFIG_SMP if (unlikely((res & (LAST_INO_BATCH-1 )) == 0 )) { static atomic_t shared_last_ino; int next = atomic_add_return(LAST_INO_BATCH, &shared_last_ino); res = next - LAST_INO_BATCH; } #endif res++; if (unlikely(!res)) res++; *p = res; put_cpu_var(last_ino); return res; } EXPORT_SYMBOL(get_next_ino);
inode_init_owner 根据POSIX标准初始化新inode的uid、gid和mode 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 void inode_init_owner (struct mnt_idmap *idmap, struct inode *inode, const struct inode *dir, umode_t mode) { inode_fsuid_set(inode, idmap); if (dir && dir->i_mode & S_ISGID) { inode->i_gid = dir->i_gid; if (S_ISDIR(mode)) mode |= S_ISGID; } else inode_fsgid_set(inode, idmap); inode->i_mode = mode; } EXPORT_SYMBOL(inode_init_owner);
VFS Inode时间戳管理:高精度与并发安全的更新机制 本代码片段是Linux VFS层中负责管理inode时间戳的核心函数,重点关注ctime
(change time,状态改变时间)的更新。其核心功能是提供一套健壮、高效且并发安全的API,用于将inode的ctime
设置为特定值或当前时间。这套机制通过引入时间精度(granularity)处理、性能优化的“多粒度时间”(multigrain time)概念以及原子操作,解决了在现代多核、高精度时钟环境下,正确且高效地更新文件元数据的复杂问题。
实现原理分析 该机制的实现是分层且高度优化的,旨在平衡精度、性能和并发安全性。
基础设置器 (inode_set_ctime_to_ts
) :
这是最底层的API,负责将一个明确给定的timespec64
值设置到inode的i_ctime_sec
和i_ctime_nsec
字段中。它不包含任何复杂逻辑,仅仅是执行赋值操作,是其他高层函数的基础。
精度裁剪 (timestamp_truncate
) :
不同的文件系统(如ext4, vfat, ntfs)对时间戳的支持精度不同。例如,FAT32的精度只有2秒。此函数的作用就是将一个高精度的时间戳,根据其所在文件系统的能力(由inode->i_sb->s_time_gran
定义),“裁剪”到合法的精度。它通过取模运算(%
)实现,确保写入存储介质的时间戳不会超出文件系统的表示范围。
高性能当前时间更新 (inode_set_ctime_current
) :
这是最核心和最复杂的函数,其目标是以最小的开销将ctime
更新为“现在”。它实现了一个“多粒度时间”(multigrain time)的优化策略: a. 优先使用粗粒度时间 : 它首先获取一个低精度的“粗粒度”当前时间(ktime_get_coarse_real_ts64_mg
)。这是一个非常快速的操作,因为它读取的是一个被内核定期更新的缓存值,通常不需要昂贵的时钟源访问。 b. 惰性获取高精度时间 : 只有在必要时,它才会去获取高精度的当前时间(ktime_get_real_ts64_mg
),这是一个相对较慢的操作。那么何时是“必要”的呢?当且仅当:1) 已经有人查询过这个inode的精确时间戳(通过I_CTIME_QUERIED
标志位判断);并且 2) 当前的粗粒度时间并不比inode上已有的时间戳更新。这个逻辑避免了在绝大多数情况下(即短时间内连续的、无人关心的更新)调用昂贵的高精度时间函数。 c. 并发安全更新 : 在多核系统中,多个CPU可能同时尝试更新同一个inode的ctime
。一个简单的赋值操作会导致“最后写入者获胜”的竞争,并可能丢失更新。为了解决这个问题,该函数不直接赋值,而是使用原子操作try_cmpxchg
来尝试交换纳秒字段。如果交换成功,说明我们成功地写入了新值。如果失败,意味着在cmpxchg
执行的瞬间,已有另一个CPU写入了一个更新的时间戳。在这种情况下,函数会接受这个已经存在的新值,因为它保证了时间的单调递增,从而确保了ctime
的正确性。
代码分析 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 struct timespec64 inode_set_ctime_to_ts (struct inode *inode, struct timespec64 ts) { trace_inode_set_ctime_to_ts(inode, &ts); set_normalized_timespec64(&ts, ts.tv_sec, ts.tv_nsec); inode->i_ctime_sec = ts.tv_sec; inode->i_ctime_nsec = ts.tv_nsec; return ts; } EXPORT_SYMBOL(inode_set_ctime_to_ts); struct timespec64 timestamp_truncate (struct timespec64 t, struct inode *inode) { struct super_block *sb = inode->i_sb; unsigned int gran = sb->s_time_gran; t.tv_sec = clamp(t.tv_sec, sb->s_time_min, sb->s_time_max); if (unlikely(t.tv_sec == sb->s_time_max || t.tv_sec == sb->s_time_min)) t.tv_nsec = 0 ; if (gran == 1 ) ; else if (gran == NSEC_PER_SEC) t.tv_nsec = 0 ; else if (gran > 1 && gran < NSEC_PER_SEC) t.tv_nsec -= t.tv_nsec % gran; else WARN(1 , "invalid file time granularity: %u" , gran); return t; } EXPORT_SYMBOL(timestamp_truncate); struct timespec64 inode_set_ctime_current (struct inode *inode) { struct timespec64 now ; u32 cns, cur; ktime_get_coarse_real_ts64_mg(&now); now = timestamp_truncate(now, inode); if (!is_mgtime(inode)) { inode_set_ctime_to_ts(inode, now); goto out; } cns = smp_load_acquire(&inode->i_ctime_nsec); if (cns & I_CTIME_QUERIED) { struct timespec64 ctime = { .tv_sec = inode->i_ctime_sec, .tv_nsec = cns & ~I_CTIME_QUERIED }; if (timespec64_compare(&now, &ctime) <= 0 ) { ktime_get_real_ts64_mg(&now); now = timestamp_truncate(now, inode); mgtime_counter_inc(mg_fine_stamps); } } mgtime_counter_inc(mg_ctime_updates); if (cns == now.tv_nsec && inode->i_ctime_sec == now.tv_sec) { trace_ctime_xchg_skip(inode, &now); goto out; } cur = cns; retry: if (try_cmpxchg(&inode->i_ctime_nsec, &cur, now.tv_nsec)) { inode->i_ctime_sec = now.tv_sec; trace_ctime_ns_xchg(inode, cns, now.tv_nsec, cur); mgtime_counter_inc(mg_ctime_swaps); } else { if (!(cns & I_CTIME_QUERIED) && (cns | I_CTIME_QUERIED) == cur) { cns = cur; goto retry; } now.tv_sec = inode->i_ctime_sec; now.tv_nsec = cur & ~I_CTIME_QUERIED; } out: return now; } EXPORT_SYMBOL(inode_set_ctime_current);