这篇文章介绍了VFS - struct file:Linux文件系统 struct file 结构体解析
接下来介绍VFS - struct inode:
在Linux内核的虚拟文件系统(VFS)中,一个dentry(目录项)通常包含一个指向inode的指针。inode是文件系统中的对象,例如常规文件、目录、FIFO(命名管道)等。inode可以存在于磁盘上(用于块设备文件系统)或内存中(用于伪文件系统)。当需要时,磁盘上的inode会被复制到内存中,并且对inode的更改会被写回磁盘。一个inode可以被多个dentry指向(例如,硬链接就是这样)。
要查找一个inode,VFS需要调用父目录inode的lookup()方法。这个方法由inode所在的具体文件系统实现安装。一旦VFS获取到所需的dentry(从而获取到inode),我们就可以执行诸如使用open()打开文件或使用stat()查看inode数据等常见操作。stat()操作相对简单:一旦VFS获得了dentry,它会查看inode数据并将其中的一部分返回给用户空间。
总体而言,VFS通过抽象底层细节并提供对inode和dentry的通用操作,为不同类型的文件系统提供了统一的接口。这样做可以方便地与文件系统进行交互。
inode包含了文件系统各种对象(文件、目录、块设备文件、字符设备文件等)的元数据。每个文件和目录在文件系统中都有一个唯一的索引节点号(Inode number),用于标识和引用该文件或目录。索引节点号在文件系统中是唯一的,通过它可以准确地找到对应的索引节点。
索引节点存储了与文件或目录相关的元数据信息,包括文件类型、权限、所有者、大小、时间戳等。这些元数据信息描述了文件的属性和特征。
对于基于磁盘的文件系统,inode存在于磁盘上,其形式取决于文件系统的类型。在打开该对象进行访问时,其inode被读入内存。在打开对象时,还根据文件系统类型和对象类型设置inode操作表和file操作表。
内存中inode有一部分是各种文件系统共有的,通常称为VFS inode,即索引节点对象。
之后对VFS inode的任何修改都将写回磁盘更新磁盘的索引节点。
一个索引节点代表了文件系统的一个文件,在文件创建时创建文件删除时销毁,但是索引节点对象(VFS inode)仅在当文件被访问时,才在内存中创建,且无论有多少个副本访问这个文件,inode只存在一份,VFS inode也只存在一份。
索引节点占用磁盘空间,VFS inode占用内存空间。
VFS inode是各种具体文件系统的对象的抽象,对于具体的文件系统,另外有两种形式的inode,一种是内存inode,另一种是磁盘inode。两者之间相互转换,用在不同的场合下。
因此有三种inode:
(1)磁盘上具体的文件系统的inode。
(2)内存中具体的文件系统的inode,具体的文件系统的索引节点对象,需要包括VFS inode。
(3)VFS inode,索引节点对象。
比如ext4文件系统:
磁盘上ext4文件系统的inode:
/*
* Structure of an inode on the disk
*/
struct ext4_inode {
......
}
内存中ext4文件系统的inode:
/*
* fourth extended file system inode data in memory
*/
struct ext4_inode_info {
......
struct inode vfs_inode;
......
}
inode描述了Linux VFS层使用的inode对象。对于具体的文件系统,需要定义自己的索引节点对象,定义时以VFS inode作为一个域。
VFS inode:
/*
* Keep mostly read-only and often accessed (especially for
* the RCU path lookup and 'stat' data) fields at the beginning
* of the 'struct inode'
*/
struct inode {
......
}
const struct file_operations ext4_file_operations = {
.llseek = ext4_llseek,
.read_iter = ext4_file_read_iter,
.write_iter = ext4_file_write_iter,
.unlocked_ioctl = ext4_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = ext4_compat_ioctl,
#endif
.mmap = ext4_file_mmap,
.mmap_supported_flags = MAP_SYNC,
.open = ext4_file_open,
.release = ext4_release_file,
.fsync = ext4_sync_file,
.get_unmapped_area = thp_get_unmapped_area,
.splice_read = generic_file_splice_read,
.splice_write = iter_file_splice_write,
.fallocate = ext4_fallocate,
};
const struct inode_operations ext4_file_inode_operations = {
.setattr = ext4_setattr,
.getattr = ext4_file_getattr,
.listxattr = ext4_listxattr,
.get_acl = ext4_get_acl,
.set_acl = ext4_set_acl,
.fiemap = ext4_fiemap,
};
索引节点对象(struct inode)代表着一个具体的文件。inode(索引节点)对象在Linux内核中表示所有内核操作文件或目录所需的信息。对于类Unix风格的文件系统,这些信息直接从磁盘上的inode读取。然而,对于没有使用inode的文件系统,文件系统需要从磁盘的其他位置获取所需的信息。
在传统的Unix文件系统中,磁盘上的inode包含有关文件或目录的元数据,如大小、所有权、权限、时间戳以及指向存储文件实际内容的数据块的指针。
对于Unix风格的文件系统,内核操作文件或目录所需的信息可以直接从磁盘索引节点直接读入。
Linux内核中的inode对象反映了这个结构,并保存了内存中磁盘上inode的表示。
比如,对于ext4文件系统,内核操作文件或目录所需的信息时,索引节点对象struct inode直接从磁盘上的inode - struct ext4_inode(Structure of an inode on the disk) 中读取。
当访问或修改文件或目录时,内核与inode对象交互以执行必要的操作。inode对象跟踪文件的各种属性和状态,包括大小、时间戳、链接计数、所有权和其他相关信息。后续会将修改的内容写回磁盘更新磁盘的索引节点。
值得注意的是,并非所有的Linux文件系统都必须使用inode。一些现代文件系统,如Btrfs或ZFS,使用不同的数据结构来表示文件和目录。然而,inode对象的概念仍然是Linux内核文件管理系统的基本组成部分,为跨不同文件系统访问和操作文件提供了统一的接口。
VFS实现了open()、stat()、chmod()等系统调用。这些系统调用中传递的路径名参数被VFS用于通过目录项缓存(也称为dentry缓存或dcache)进行搜索。这提供了一种非常快速的查找机制,将路径名(文件名)转换为特定的dentry。Dentries存在于RAM中,从不保存到磁盘:它们仅存在于性能优化的目的。
目录项缓存旨在提供对整个文件空间的视图。由于大多数计算机无法同时将所有的dentry都放入RAM中,因此缓存中可能会缺少一部分数据。为了将路径名解析为dentry,VFS可能需要在路径上创建dentry,并加载相应的inode。这是通过查找inode来完成的。
当VFS遇到一个路径名时,它首先会查看目录项缓存,看是否存在所需的dentry。如果存在,它可以直接使用该dentry并加载相应的inode。但是,如果目录项缓存中没有所需的dentry,VFS需要进行一系列的操作来解析路径名。
首先,VFS会从根目录开始,通过调用父目录inode的lookup()方法来查找下一个目录项。这个过程会递归地进行,直到找到路径名中的最后一个目录项。
在这个过程中,VFS会创建新的dentry,并将其插入到目录项缓存中,以便以后再次使用。然后,VFS会加载与新创建的dentry对应的inode,并将其与dentry关联起来。
通过递归查找和创建dentry,VFS最终能够将路径名解析为一个特定的dentry,并加载相应的inode。
需要注意的是,目录项缓存是一个动态的结构,它会根据系统的需求进行调整和更新。当系统资源有限时,VFS可能会根据某种策略选择要保留在缓存中的dentry,以提高整体性能。
总结起来,VFS通过目录项缓存提供了一个快速的查找机制,将路径名转换为特定的dentry,并通过查找inode来加载相应的文件。这样可以提高文件系统操作的效率。
/*
* Keep mostly read-only and often accessed (especially for
* the RCU path lookup and 'stat' data) fields at the beginning
* of the 'struct inode'
*/
struct inode {
umode_t i_mode;
unsigned short i_opflags;
kuid_t i_uid;
kgid_t i_gid;
unsigned int i_flags;
#ifdef CONFIG_FS_POSIX_ACL
struct posix_acl *i_acl;
struct posix_acl *i_default_acl;
#endif
const struct inode_operations *i_op;
struct super_block *i_sb;
struct address_space *i_mapping;
#ifdef CONFIG_SECURITY
void *i_security;
#endif
/* Stat data, not accessed from path walking */
unsigned long i_ino;
/*
* Filesystems may only read i_nlink directly. They shall use the
* following functions for modification:
*
* (set|clear|inc|drop)_nlink
* inode_(inc|dec)_link_count
*/
union {
const unsigned int i_nlink;
unsigned int __i_nlink;
};
dev_t i_rdev;
loff_t i_size;
struct timespec64 i_atime;
struct timespec64 i_mtime;
struct timespec64 i_ctime;
spinlock_t i_lock; /* i_blocks, i_bytes, maybe i_size */
unsigned short i_bytes;
u8 i_blkbits;
u8 i_write_hint;
blkcnt_t i_blocks;
#ifdef __NEED_I_SIZE_ORDERED
seqcount_t i_size_seqcount;
#endif
/* Misc */
unsigned long i_state;
struct rw_semaphore i_rwsem;
unsigned long dirtied_when; /* jiffies of first dirtying */
unsigned long dirtied_time_when;
struct hlist_node i_hash;
struct list_head i_io_list; /* backing dev IO list */
#ifdef CONFIG_CGROUP_WRITEBACK
struct bdi_writeback *i_wb; /* the associated cgroup wb */
/* foreign inode detection, see wbc_detach_inode() */
int i_wb_frn_winner;
u16 i_wb_frn_avg_time;
u16 i_wb_frn_history;
#endif
struct list_head i_lru; /* inode LRU list */
struct list_head i_sb_list;
struct list_head i_wb_list; /* backing dev writeback list */
union {
struct hlist_head i_dentry;
struct rcu_head i_rcu;
};
atomic64_t i_version;
atomic_t i_count;
atomic_t i_dio_count;
atomic_t i_writecount;
#if defined(CONFIG_IMA) || defined(CONFIG_FILE_LOCKING)
atomic_t i_readcount; /* struct files open RO */
#endif
union {
const struct file_operations *i_fop; /* former ->i_op->default_file_ops */
void (*free_inode)(struct inode *);
};
struct file_lock_context *i_flctx;
struct address_space i_data;
struct list_head i_devices;
union {
struct pipe_inode_info *i_pipe;
struct block_device *i_bdev;
struct cdev *i_cdev;
char *i_link;
unsigned i_dir_seq;
};
__u32 i_generation;
#ifdef CONFIG_FSNOTIFY
__u32 i_fsnotify_mask; /* all events this inode cares about */
struct fsnotify_mark_connector __rcu *i_fsnotify_marks;
#endif
#ifdef CONFIG_FS_ENCRYPTION
struct fscrypt_info *i_crypt_info;
#endif
#ifdef CONFIG_FS_VERITY
struct fsverity_info *i_verity_info;
#endif
void *i_private; /* fs or device private pointer */
} __randomize_layout;
i_mode:该字段表示文件的访问权限和类型。它包括读取、写入和执行权限的信息,以及条目是否为常规文件、目录、符号链接等。
i_opflags:该字段包含提供有关索引节点及其操作的附加信息的标志。
i_uid和i_gid:这些字段分别存储文件所有者的用户ID(UID)和组ID(GID)。
i_flags:该字段存储与索引节点关联的各种标志,例如是否为不可变的、仅追加的或大文件。
i_acl和i_default_acl:这些字段是指向与文件关联的POSIX访问控制列表(ACL)的指针。ACL提供了超出传统Unix权限的细粒度访问控制。
Linux中的ACL(Access Control List)是一种扩展文件权限模型,允许更精细地控制文件和目录的访问权限。ACL可以为文件或目录添加额外的访问规则,以授权特定用户或用户组具有特定的权限。
i_op:该字段是指向类型为struct inode_operations的结构的指针,该结构包含可在索引节点上执行的各种操作的函数指针。
i_sb:该字段是指向与索引节点关联的超级块的指针。超级块表示文件系统实例,并包含有关文件系统的信息,例如其类型、块大小和挂载设备。
i_mapping:该字段是指向与索引节点关联的地址空间对象的指针。地址空间管理文件数据与磁盘块之间的映射。是用于描述页高速缓存中的页面的一个文件对应一个address_space,一个address_space与一个偏移量能够确定一个也高速缓存中的页面。i_mapping通常指向i_data,不过两者是有区别的,i_mapping表示应该向谁请求页面,i_data表示被改inode读写的页面。
i_security:该字段是指向与索引节点关联的与安全相关的信息的指针。它由安全模块用于存储附加的安全属性。
i_ino:该字段存储索引节点号,这是文件系统内唯一的标识符。每个VFS inode(对给定的文件系统)都由一个唯一的编号标识,保存在i_ino中。
i_nlink:指向索引节点的硬链接数。文件系统应直接读取此字段,但使用特定的函数进行修改。
i_rdev:如果索引节点表示特殊文件(例如字符设备或块设备),则该字段存储与文件关联的设备号。在inode表示设备文件时,则需要i_rdev。它表示与哪个设备进行通信。要注意,i_rdev只是一
个数字,不是数据结构!但这个数字包含的信息,即足以找到有关目标设备、我们感兴趣的所有信息。对块设备,最终会找到struct block_device的一个实例。
如果inode表示设备特殊文件,那么i_rdev之后的匿名联合就包含了指向设备专用数据结构的指针。
i_size:文件的大小(以字节为单位)。
i_atime、i_mtime、t_ctime分别存储了最后访问文件的时间、最后修改文件的时间、最后修改inode的时间。修改文件着修改与inode相关的数据段内容。修改inode意味着修改inode结构自身(或文件的某个属性),这导致了i_ctime的改变。
i_lock:此自旋锁用于保护索引节点的各个字段,例如i_blocks、i_bytes和i_size,以免并发修改。
i_bytes:以512字节(2^9)的块为单位,文件最后一个块的字节数。
i_blkbits:以bit为单位的块的大小。
i_write_hint:对底层存储层关于索引节点访问模式的提示。它提供有关文件是主要读取还是写入的信息。
i_blocks:分配给文件的磁盘块数,文件使用块的数目。是文件系统的特征,不属于文件自身。在许多文件系统创建时,会选择一个块长度,作为在硬件介质上分配存储空间的最小单位。因此,按块计算的文件长度,也可以根据文件的字节长度和文件系统块长度计算得出。实际上没有这样做,为方便起见,该信息也归入到inode结构。
i_state:该字段存储与索引节点关联的各种状态标志,例如它是否为脏位或被引用。
i_rwsem:此读写信号量用于同步对索引节点的访问,允许同时进行多个读取或单个写入。
dirtied_when和dirtied_time_when:这些字段记录索引节点首次被标记为脏位的时间。
i_hash:此字段用于将索引节点链接到索引节点哈希表。
i_io_list:此字段用于将索引节点链接到当前正在进行I/O操作的索引节点列表。
i_lru:此字段用于将索引节点链接到最近最少使用(LRU)列表,用于缓存管理。
i_sb_list:此字段用于将索引节点链接到与特定超级块相关的索引节点列表。
i_wb_list:此字段用于将索引节点链接到当前正在进行写回操作的索引节点列表。
i_dentry:此字段用于将索引节点链接到目录项(dentry)列表。
i_version:此原子变量用于实现文件版本控制,允许跟踪文件的更改。
i_count、i_dio_count、i_writecount、i_readcount:这些原子变量用于对索引节点进行引用计数,并跟踪特定类型的引用,如直接I/O引用。i_count是一个使用计数器,指定访问该inode结构的进程数目。例如,进程通过fork复制自身时,inode会由不同进程同时使用。
i_fop:该字段要么是与文件类型关联的默认文件操作(struct file_operations)结构的指针,要么是与文件系统特定的文件操作结构的指针。
i_flctx:该字段用于存储文件锁定上下文。
i_data:该字段是表示索引节点数据地址空间的地址空间对象。它用于将文件的数据块映射到磁盘块。
i_devices:该字段用于将索引节点链接到设备列表。
i_pipe、i_bdev、i_cdev、i_link、i_dir_seq:这些字段用于特定类型的文件,如管道、块设备、字符设备或符号链接。
i_bdev用于块设备,i_pipe包含了用于实现管道的inode的相关信息,而i_cdev用于字符设备。
由于一个inode一次只能表示一种类型的设备,所以将i_pipe、i_bdev和i_cdev放置在联合中是安全
的。i_devices也与设备文件的处理有关联:利用该成员作为链表元素,使得块设备或字符设备可以维护一个inode的链表,每个inode表示一个设备文件,通过设备文件可以访问对应的设备。尽管在很多情况下每个设备一个设备文件就足够了,但还有很多种可能性。例如chroot造成的环境,其中一个给定的块设备或字符设备可以通过多个设备文件,因而需要多个inode。
i_generation:该字段存储索引节点的生成编号。它通常用于NFS(网络文件系统)以检测过期的文件句柄。
i_fsnotify_mask和i_fsnotify_marks:这些字段用于文件系统通知(fsnotify),用于跟踪注册的通知事件和相关的标记。
i_crypt_info:此字段用于存储与文件加密相关的信息。
i_verity_info:此字段用于存储与文件完整性验证(verity)相关的信息。
i_private:该字段是与文件系统特定或设备特定的私有数据与索引节点关联的指针。
struct inode结构体有几个链表成员:
(1)
struct hlist_node i_hash;
除根目录的inode外,所有inode对象(这里的indo指的时vfs inode,在访问文件时在内存中生成)在一个散列表中出现,加入到一个全局哈希表inode_hashtable中,其哈希项的索引计算基于所属文件系统的超级块描述符地址,以及inode编号。以支持根据inode编号和超级块快速访问inode,这两项的组合在系统范围内是唯一的。这个哈希表的作用是方便查找属于某个文件系统的特定inode。
// linux-5.4.18/fs/inode.c
static struct hlist_head *inode_hashtable __read_mostly;
/*
* Initialize the waitqueues and inode hash table.
*/
void __init inode_init_early(void)
{
/* If hashes are distributed across NUMA nodes, defer
* hash allocation until vmalloc space is available.
*/
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);
}
void __init inode_init(void)
{
/* inode slab cache */
inode_cachep = kmem_cache_create("inode_cache",
sizeof(struct inode),
0,
(SLAB_RECLAIM_ACCOUNT|SLAB_PANIC|
SLAB_MEM_SPREAD|SLAB_ACCOUNT),
init_once);
/* Hash may have been set up in inode_init_early */
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);
}
/**
* __insert_inode_hash - hash an inode
* @inode: unhashed inode
* @hashval: unsigned long value used to locate this object in the
* inode_hashtable.
*
* Add an inode to the inode hash for this superblock.
*/
void __insert_inode_hash(struct inode *inode, unsigned long hashval)
{
struct hlist_head *b = inode_hashtable + hash(inode->i_sb, hashval);
spin_lock(&inode_hash_lock);
spin_lock(&inode->i_lock);
hlist_add_head(&inode->i_hash, b);
spin_unlock(&inode->i_lock);
spin_unlock(&inode_hash_lock);
}
EXPORT_SYMBOL(__insert_inode_hash);
__insert_inode_hash函数用于将一个索引节点(inode)插入到指定超级块(superblock)的索引节点哈希表中。
(2)
struct list_head i_io_list; /* backing dev IO list */
用于将索引节点链接到当前正在进行I/O操作的索引节点列表。
(3)
struct list_head i_lru; /* inode LRU list */
用于将索引节点链接到最近最少使用(LRU)列表,用于缓存管理。
(4)
struct list_head i_sb_list;
除了散列表之外,inode还通过一个特定于超级块的链表维护,表头是super_block->s_inodes。
i_sb_list用作链表元素。
每个inode被包含在所属文件系统的super_block 以s_inodes域为首的双循环链表中,inode的i_sb_list域保存了在该链表中的相连元素的指针。
struct super_block {
......
/* s_inode_list_lock protects s_inodes */
spinlock_t s_inode_list_lock ____cacheline_aligned_in_smp;
struct list_head s_inodes; /* all inodes */
}
(5)
struct list_head i_wb_list; /* backing dev writeback list */
用于将索引节点链接到当前正在进行写回操作的索引节点列表,该链表通常与写入回写(writeback)机制相关。
struct super_block {
......
spinlock_t s_inode_wblist_lock;
struct list_head s_inodes_wb; /* writeback inodes */
}
每个writeback inodes被包含在所属文件系统的super_block 以s_inodes_wb域为首的双循环链表中,inode的i_wb_list 域保存了在该链表中的相连元素的指针。
这样做有下列好处:在写回数据时(数据回写通常也称之为同步)不需要扫描系统所有的inode,考虑writeback list上所有的inode就足够了。
这里就是将所有的inode分为几种不同类型的inode,分别链接到对应的链表上,当需要查找对应类型的inode时去相应的链表找即可,不需要去 s_inodes - all inodes list 查找。
(6)
struct hlist_head i_dentry;
硬链接可以让一个inode(一个文件)会对应多个目录项,i_dentry用于管理该inode的多个目录项。i_dentry引用这个inode的dentry链表的表头。dentry结构的d_alias域链入到所属inode的i_dentry(别名)链表的“连接件”。
struct dentry {
......
struct hlist_node d_alias; /* inode alias list */
......
}
(7)
struct list_head i_devices;
union {
struct pipe_inode_info *i_pipe;
struct block_device *i_bdev;
struct cdev *i_cdev;
......
};
inode可以表示多种对象,包括目录、文件、符号链接、字符设备、块设备等。
如果inode表示块设备,则i_rdev保存了块设备编号,i_bdev为指向块设备描述符(block_device)的指针,同时通过“连接件”i_devices链入到块设备的inodes链表。
如果inode表示字符设备,则i_rdev保存了字符设备编号,i_cdev为指向字符设备描述符(cdev)的指针,同时通过i_devices链入到链入到字符设备的list链表。
struct inode_operations {
struct dentry * (*lookup) (struct inode *,struct dentry *, unsigned int);
const char * (*get_link) (struct dentry *, struct inode *, struct delayed_call *);
int (*permission) (struct inode *, int);
struct posix_acl * (*get_acl)(struct inode *, int);
int (*readlink) (struct dentry *, char __user *,int);
int (*create) (struct inode *,struct dentry *, umode_t, bool);
int (*link) (struct dentry *,struct inode *,struct dentry *);
int (*unlink) (struct inode *,struct dentry *);
int (*symlink) (struct inode *,struct dentry *,const char *);
int (*mkdir) (struct inode *,struct dentry *,umode_t);
int (*rmdir) (struct inode *,struct dentry *);
int (*mknod) (struct inode *,struct dentry *,umode_t,dev_t);
int (*rename) (struct inode *, struct dentry *,
struct inode *, struct dentry *, unsigned int);
int (*setattr) (struct dentry *, struct iattr *);
int (*getattr) (const struct path *, struct kstat *, u32, unsigned int);
ssize_t (*listxattr) (struct dentry *, char *, size_t);
int (*fiemap)(struct inode *, struct fiemap_extent_info *, u64 start,
u64 len);
int (*update_time)(struct inode *, struct timespec64 *, int);
int (*atomic_open)(struct inode *, struct dentry *,
struct file *, unsigned open_flag,
umode_t create_mode);
int (*tmpfile) (struct inode *, struct dentry *, umode_t);
int (*set_acl)(struct inode *, struct posix_acl *, int);
} ____cacheline_aligned;
struct inode_operations 是 Linux 内核中的一个结构体,用于定义与索引节点(inode)相关的操作函数。它包含了一系列函数指针,用于实现文件系统的各种操作,如查找文件、创建文件、删除文件、读取链接目标等。
下面是 struct inode_operations 结构体中定义的一些常见操作函数及其功能:
lookup: 在给定的目录中查找指定名称的文件或目录。
get_link: 获取符号链接文件的目标路径。
permission: 检查是否具有对文件执行给定操作的权限。
get_acl: 获取与文件相关的 POSIX ACL(访问控制列表)。
readlink: 读取符号链接文件的目标路径。
create: 创建新文件。
link: 创建硬链接。
unlink: 删除链接。
symlink: 创建符号链接。
mkdir: 创建新目录。
rmdir: 删除目录。
mknod: 创建设备节点。
rename: 重命名文件或目录。
setattr: 设置文件属性。
getattr: 获取文件属性。
listxattr: 列出文件的扩展属性。
fiemap: 获取文件的区域映射。
update_time: 更新文件的访问和修改时间。
atomic_open: 打开文件或创建文件(原子操作)。
tmpfile: 创建临时文件。
set_acl: 设置文件的 POSIX ACL。
上述函数指针定义了文件系统实现所需的操作,并根据具体的文件系统类型进行相应的填充。通过实现这些函数,文件系统可以对索引节点执行特定的操作。
请注意,这只是 struct inode_operations 结构体的一个示例,实际的文件系统可能会定义和实现其他操作函数。
(1)
// linux-5.4.18/fs/stat.c
static inline loff_t __inode_get_bytes(struct inode *inode)
{
return (((loff_t)inode->i_blocks) << 9) + inode->i_bytes;
}
loff_t inode_get_bytes(struct inode *inode)
{
loff_t ret;
spin_lock(&inode->i_lock);
ret = __inode_get_bytes(inode);
spin_unlock(&inode->i_lock);
return ret;
}
inode_get_bytes 函数调用 __inode_get_bytes 函数来获取索引节点的字节总数。
内联函数 __inode_get_bytes,该函数接受一个指向 struct inode 的指针作为参数,并返回一个 loff_t 类型的值,即 64 位的偏移量。
该函数通过以下方式计算索引节点的字节总数:
inode->i_blocks 是索引节点中分配的磁盘块数。
inode->i_bytes 是索引节点中最后一个块的字节偏移量。
乘以 512(<< 9 是位操作,相当于乘以 2 的 9 次方),然后将两者相加,得到索引节点的字节总数。
void inode_set_bytes(struct inode *inode, loff_t bytes)
{
/* Caller is here responsible for sufficient locking
* (ie. inode->i_lock) */
inode->i_blocks = bytes >> 9;
inode->i_bytes = bytes & 511;
}
EXPORT_SYMBOL(inode_set_bytes);
函数内部,它将字节总数 bytes 右移 9 位(相当于除以 512,将块数计算出来),并将结果存储在 inode->i_blocks 中。然后,它将 bytes 与 511 进行按位与操作(相当于取模 512),将结果存储在 inode->i_bytes 中。
通过调用 inode_set_bytes 函数,可以设置给定索引节点的字节总数。
(2)根据 struct file 获取 struct inode:
static inline struct inode *file_inode(const struct file *f)
{
return f->f_inode;
}
/**
* d_inode - Get the actual inode of this dentry
* @dentry: The dentry to query
*
* This is the helper normal filesystems should use to get at their own inodes
* in their own dentries and ignore the layering superimposed upon them.
*/
static inline struct inode *d_inode(const struct dentry *dentry)
{
return dentry->d_inode;
}
struct file {
struct path f_path;
struct inode *f_inode; /* cached value */
}
struct path {
struct vfsmount *mnt;
struct dentry *dentry;
} __randomize_layout;
struct dentry {
......
struct inode *d_inode;
} __randomize_layout;
因此根据 struct file 获取 struct inode 有两种方案:
struct file *file;
struct inode * inode = file->f_inode;
struct inode * inode = file->f_path.dentry->d_inode;
Linux 5.4.18
https://static.lwn.net/kerneldoc/filesystems/vfs.html