结构概观
知识点1:
VFS由两个部分组成,文件和文件系统,这都需要管理和抽象。
文件的表示
inode
是内核选择用于表示文件内容和相关元数据的方法。
**inode操作:**创建连接、文件重命名、在目录中生成新文件、删除文件。
**文件操作:**作用于文件的数据内容。包含一些显然的操作(如读和写),还包括设置文件位置指针和创建内存映射之类的操作。
上面的图中,一共有两条线路可以走。
- 从内核出发的一条路,super_block->f_dentry->i_dentry->inode->address_space
- 从进程出发的一条路,task_struct->file_struct->f_dentry->inode->address_space
结构体task_struct
中,包含成员files
用于保存所有打开的文件,此成员是一个数组,访问时利用文件描述符作为索引。各个数组项包含的对象不仅关联对应文件的inode
,还包含一个指针,指向用于加速查找操作的目录项缓存的一个成员。打开的文件总是分配到系统中的一个特定的进程,内核必须在数据结构中存储文件和进程之间的关联。
内核中还需要其他结构来保存和inode
相关的信息,特别重要的是与每个inode
关联的数据段,都存储了文件的内容和目录项表。每个inode
还包含了指向底层文件系统的超级块对象的指针,用于执行对inode
本身的操作。
文件系统和超级块信息
VFS支持的文件系统类型通过一种特殊的内核对象连接起来,该对象提供了一种读取超级块的方法,超级块中包含了文件系统的关键信息,包括,块长度,最大文件长度。与此同时,还包
含了读、写inode
的函数指针。
内核中还建立了一个链表,用来存放所有活动文件的超级块实例。
进一步的,超级块实例中存放着一个列表,保存文件系统中所有修改过的inode
,也即脏inode
。这个列表的作用是很容易标识已经修改过的文件和目录,以便将其写回到存储介质中。
补充知识点:
回写必须经过协调,保证一定程度上最小化开销,因为这是非常费时的操作。另一方面,如果写回数据的间隔太长,可能导致数据丢失。如系统崩溃。所以,内核会周期性的扫描脏块的列表,并将修改传输到底层硬件。
inode
VFS的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 timespec i_atime;
struct timespec i_mtime;
struct timespec i_ctime;
spinlock_t i_lock; /* i_blocks, i_bytes, maybe i_size */
unsigned short i_bytes;
unsigned int i_blkbits;
#if defined(CONFIG_IMA) && (defined(CONFIG_PPC64) || defined(CONFIG_S390))
/* 4 bytes hole available on both required architectures */
RH_KABI_FILL_HOLE(atomic_t i_readcount)
#endif
blkcnt_t i_blocks;
#ifdef __NEED_I_SIZE_ORDERED
seqcount_t i_size_seqcount;
#endif
/* Misc */
unsigned long i_state;
struct mutex i_mutex;
unsigned long dirtied_when; /* jiffies of first dirtying */
struct hlist_node i_hash;
RH_KABI_RENAME(struct list_head i_wb_list,
struct list_head i_io_list); /* backing dev IO list */
struct list_head i_lru; /* inode LRU list */
struct list_head i_sb_list;
union {
struct hlist_head i_dentry;
struct rcu_head i_rcu;
};
u64 i_version;
atomic_t i_count;
atomic_t i_dio_count;
atomic_t i_writecount;
const struct file_operations *i_fop; /* former ->i_op->default_file_ops */
struct file_lock *i_flock;
struct address_space i_data;
#ifdef CONFIG_QUOTA
struct dquot *i_dquot[MAXQUOTAS];
#endif
struct list_head i_devices;
union {
struct pipe_inode_info *i_pipe;
struct block_device *i_bdev;
struct cdev *i_cdev;
};
__u32 i_generation;
#ifdef CONFIG_FSNOTIFY
__u32 i_fsnotify_mask; /* all events this inode cares about */
RH_KABI_REPLACE(struct hlist_head i_fsnotify_marks,
struct fsnotify_mark_connector __rcu *i_fsnotify_marks)
#endif
#if defined(CONFIG_IMA) && defined(CONFIG_X86_64)
atomic_t i_readcount; /* struct files open RO */
#endif
void *i_private; /* fs or device private pointer */
};
知识点1:
inode
是内存中处理的,因此包含了一些实际介质上存储的inode
所没有的成员。这些是由内核自身从底层文件系统读入信息时生成或动态建立。
知识点2:
大部分成员是元数据信息,用于管理简单的状态信息。
时间信息,i_atime
,i_mtime
, i_ctime
,分别存储了最后访问时间,最后修改时间,最后inode
修改时间。i_mtime
意味着修改与inode
相关的数据段内容,i_ctime
意味着修改inode
结构自身,导致了i_ctime
的改变。
文件长度保存在i_size
,按照字节计算。i_blocks
指定了文件按块计算的长度,此值与文件系统相关,不属于文件自身。
VFS inode
都由一个唯一的编号标识,保存在i_ino
中,i_count
是一个使用计数器,指定访问该inode
结构的进程数目。
例如,进程通过fork
复制自身时,inode
会由不同进程同时使用。i_links
也是一个计数器,记录使用inode
的硬链接总数。
文件的访问权限和所有权保存在i_mode
(文件类型和访问权限)、i_uid
和i_gid
(与该文件相关的UID
和GID
)中。
inode操作
内核中提供了大量的函数,对inode
进行操作,并为此定义了函数指针的集合(将其封装inode_operations
结构体中 )。实际数据是通过具体文件系统的实现操作的,调用接口保持不变,实际工作由特定于实现的函数完成。
inode
结构有两个指针(i_op
和i_fop
),指向实现了上述抽象的数组。一个与inode
操作有关,一个与file
操作有关。inode
最终都指向了file_operations
结构的指针。file_operations
用于操作文件中包含的数据,而inode_operations
负责管理结构性的操作(删除一个文件)和文件相关的元数据(属性)。
struct inode_operations {
struct dentry * (*lookup) (struct inode *,struct dentry *, unsigned int);
void * (*follow_link) (struct dentry *, struct nameidata *);
int (*permission) (struct inode *, int);
struct posix_acl * (*get_acl)(struct inode *, int);
int (*readlink) (struct dentry *, char __user *,int);
void (*put_link) (struct dentry *, struct nameidata *, void *);
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 *);
int (*setattr) (struct dentry *, struct iattr *);
int (*getattr) (struct vfsmount *mnt, struct dentry *, struct kstat *);
int (*setxattr) (struct dentry *, const char *,const void *,size_t,int);
ssize_t (*getxattr) (struct dentry *, const char *, void *, size_t);
ssize_t (*listxattr) (struct dentry *, char *, size_t);
int (*removexattr) (struct dentry *, const char *);
int (*fiemap)(struct inode *, struct fiemap_extent_info *, u64 start,
u64 len);
int (*update_time)(struct inode *, struct timespec *, int);
int (*atomic_open)(struct inode *, struct dentry *,
struct file *, unsigned open_flag,
umode_t create_mode, int *opened);
} ____cacheline_aligned;
lookup
根据文件系统对象的名称查找inode
实例。
unlink
用于删除文件。(补充,如果硬链接的引用计数器表明该inode
仍然被多个文件使用,则不会执行删除操作。)
xattr
函数建立、读取、删除文件的扩展属性。
truncate
修改指定inode
长度,该函数只接受一个参数,即所处理的inode
的数据结构。
truncate_range
用于截断一个范围内的块,但该操作只有共享内存文件系统支持。
follow_link
根据符号链接查找目标文件的inode
。
fallocate
用于对文件预先分配空间。