VFS文件系统相关结构体

1 file_system_type

cpp 复制代码
// <fs.h>
 
struct file_system_type { 
    const char *name; 
    int fs_flags; 
    struct super_block *(*get_sb) (struct file_system_type *, int, 
    const char *, void *, struct vfsmount *); 
    void (*kill_sb) (struct super_block *); 
    struct module *owner; 
    struct file_system_type * next; 
    struct list_head fs_supers; 
};

1.1 file_system_type相关的链表

(1)struct file_system_type * next;

各个可用的文件系统通过next成员连接起来,这里无法利用标准的链表功能,因为这是一个单链表。

1.2 几个重要的成员变量说明

我们最感兴趣的成员是fs_supers和函数指针get_sb。对于每个已经装载的文件系统,在内存中都创建了一个超级块结构。该结构保存了文件系统它本身和装载点的有关信息。由于可以装载几个同一类型的文件系统(最好的例子是home和root分区,二者的文件系统类型通常相同),同一文件系统类型可能对应了多个超级块结构,这些超级块聚集在一个链表中。fs_supers是对应的表头。

2 vfsmount

cpp 复制代码
// <mount.h> 

struct vfsmount { 
    struct list_head mnt_hash; 
    struct vfsmount *mnt_parent; /* 装载点所在的父文件系统 */ 
    struct dentry *mnt_mountpoint; /* 装载点在父文件系统中的dentry */ 
    struct dentry *mnt_root; /* 当前文件系统根目录的dentry */ 
    struct super_block *mnt_sb; /* 指向超级块的指针 */ 
    struct list_head mnt_mounts; /* 子文件系统链表 */ 
    struct list_head mnt_child; /* 链表元素,用于父文件系统中的mnt_mounts链表 */ 
    int mnt_flags; 
    /* 64位体系结构上,是一个4字节的空洞 */ 
    char *mnt_devname; /* 设备名称,例如/dev/dsk/hda1 */ 
    struct list_head mnt_list; 
    struct list_head mnt_expire; /* 链表元素,用于特定于文件系统的到期链表中 */ 
    struct list_head mnt_share; /* 链表元素,用于共享装载的循环链表 */ 
    struct list_head mnt_slave_list;/* 从属装载的链表 */ 
    struct list_head mnt_slave; /* 链表元素,用于从属装载的链表 */ 
    struct vfsmount *mnt_master; /* 指向主装载,从属装载位于master->mnt_slave_list链表上 */ 
    struct mnt_namespace *mnt_ns; /* 所属的命名空间 */ 
    /* 
    * 我们把mnt_count和mnt_expiry_mark放置在struct vfsmount的末尾,
    * 以便让这些频繁修改的字段与结构的主体处于两个不同的缓存行中
    * (这样在SMP机器上读取mnt_flags不会造成高速缓存的颠簸)
    */ 
    atomic_t mnt_count; 
    int mnt_expiry_mark; /* 如果标记为到期,则其值为true */ 
}; 

2.1 几个重要的成员变量说明

(1)struct vfsmount *mnt_parent;

装载点所在的父文件系统的vfsmount实例

(2)struct dentry *mnt_mountpoint;

装载点所在的父文件系统的dentry实例

(3)struct dentry *mnt_root;

当前文件系统根目录的dentry实例

2.2 相关的链表

2.2.1 相关成员变量

cpp 复制代码
struct vfsmount *mnt_parent; /* 装载点所在的父文件系统 */
struct list_head mnt_mounts; /* 子文件系统链表 */
struct list_head mnt_child; /* 链表元素,用于父文件系统中的mnt_mounts链表 */ 

2.2.2 链表关系图示

3 super_block

cpp 复制代码
// <fs.h> 

struct super_block { 
    struct list_head s_list; /* 将该成员置于起始处 */ 
    dev_t s_dev; /* 搜索索引,不是kdev_t */ 
    unsigned long s_blocksize; 
    unsigned char s_blocksize_bits; 
    unsigned char s_dirt; 
    unsigned long long s_maxbytes; /* 最大的文件长度 */ 
    struct file_system_type *s_type; 
    struct super_operations *s_op; 
    unsigned long s_flags; 
    unsigned long s_magic; 
    struct dentry *s_root;
    struct xattr_handler **s_xattr; 
    struct list_head s_inodes; /* 所有inode的链表 */ 
    struct list_head s_dirty; /* 脏inode的链表 */ 
    struct list_head s_io; /* 等待回写 */ 
    struct list_head s_more_io; /* 等待回写,另一个链表 */ 
    struct list_head s_files; 
    struct block_device *s_bdev;
    struct list_head s_instances; 
    char s_id[32]; /* 有意义的名字 */ 
    void *s_fs_info; /* 文件系统私有信息 */ 
    /* 创建/修改/访问时间的粒度,单位为ns(纳秒)。粒度不能大于1秒 */ 
    u32 s_time_gran; 
};

3.1 几个重要的成员变量说明

(1)struct list_head s_files;

s_files链表包含了一系列file结构,列出了该超级块表示的文件系统上所有打开的文件。内核在卸载文件系统时将参考该链表。

3.2 super_block相关的链表

结构的第一个成员s_list是一个链表元素,用于将系统中所有的超级块聚集到一个链表中。该 链表的表头是全程变量super_blocks,定义在fs/super.c中。 最后,各个超级块都连接到另一个链表中,表示同一类型文件系统的所有超级块实例,这里不考 虑底层的块设备,但链表中的超级块的文件系统类型都是相同的。表头是file_system_type结构的 fs_supers成员,s_instances用作链表元素。

4 inode

cpp 复制代码
// <fs.h> 

struct inode { 
    struct hlist_node i_hash; 
    struct list_head i_list; 
    struct list_head i_sb_list; 
    struct list_head i_dentry; 
    unsigned long i_ino; 
    atomic_t i_count; 
    unsigned int i_nlink; 
    uid_t i_uid; 
    gid_t i_gid; 
    dev_t i_rdev; 
    unsigned long i_version; 
    loff_t i_size; 
    struct timespec i_atime; 
    struct timespec i_mtime; 
    struct timespec i_ctime; 
    unsigned int i_blkbits; 
    blkcnt_t i_blocks; 
    umode_t i_mode; 
    struct inode_operations *i_op; 
    const struct file_operations *i_fop; /* 此前为->i_op->default_file_ops */ 
    struct super_block *i_sb;
    struct address_space *i_mapping;
    struct address_space i_data; 
    struct dquot *i_dquot[MAXQUOTAS];
    struct list_head i_devices; 
    union { 
        struct pipe_inode_info *i_pipe; 
        struct block_device *i_bdev; 
        struct cdev *i_cdev; 
    }; 
    int i_cindex; 
    __u32 i_generation; 
    unsigned long i_state; 
    unsigned long dirtied_when; /* 第一个脏操作发生的时间,以jiffies计算 */ 
    unsigned int i_flags; 
    atomic_t i_writecount; 
    void *i_security;
}; 

4.1 inode相关的链表

(1)struct list_head i_devices;

利用该成员作为链表元素,使得块设备或字符设备可以维护一个inode的链表,每个inode表示一个设备文件,通过设备文件可以访问对应的设备。

(2)struct list_head i_list;

可以将inode通过i_list成员存储在一个链表中,根据inode的状态,它有3种主要的情况。

① 全局变量inode_unused用作表头,该链表管理有效但非活动的inode。

② 全局变量inode_in_use用作表头,该链表管理所有使用但未改变的inode。

③ super_block中的s_dirty成员变量用作表头,该链表管理所有脏的inode。

④ 无效inode保存在一个本地链表中。(在检测到可移动设备的介质改变时,此前使用的inode就都没有意义了,另外文件系统重新装载时也会发生这种情况。)

(3)struct list_head i_sb_list;

super_block中的s_inodes成员变量用作表头,该链表管理所有打开的inode。

4.2 inode相关的哈希散列表

(1)每个inode不仅出现在特定于状态的链表中,还在一个散列表中出现,以支持根据inode编号和超级块快速访问inode,这两项的组合在系统范围内是唯一的。该散列表是一个数组,全局变量inode_hashtable是存储inode的哈希表。

(2)fs/inode.c中的hash函数用于计算散列和。它将inode编号和超级块对象的地址合并为一个唯一的编号,保证位于散列表已经分配的下标范围内。碰撞照例通过溢出链表解决。inode的成员i_hash用于管理溢出链表。

5 dentry

cpp 复制代码
// <dcache.h> 

struct dentry { 
    atomic_t d_count; 
    unsigned int d_flags; /* 由d_lock保护 */ 
    spinlock_t d_lock; /* 每个dentry的锁 */ 
    struct inode *d_inode; /* 文件名所属的inode,如果为NULL,则表示不存在的文件名 */ 
    /* 
    * 接下来的3个字段由__d_lookup处理
    * 将它们放置在这里,使之能够装填到一个缓存行中
    */ 
    struct hlist_node d_hash; /* 用于查找的散列表 */ 
    struct dentry *d_parent; /* 父目录的dentry实例 */ 
    struct qstr d_name; 
    struct list_head d_lru; /* LRU链表 */ 
    union { 
    struct list_head d_child; 
    /* 链表元素,用于将当前dentry连接到父目录dentry的d_subdirs链表中 */ 
    struct rcu_head d_rcu; 
    } d_u; 
    struct list_head d_subdirs; /* 子目录/文件的目录项链表 */ 
    struct list_head d_alias; /* 链表元素,用于将dentry连接到inode的i_dentry链表中 */ 
    unsigned long d_time; /* 由d_revalidate使用 */ 
    struct dentry_operations *d_op; 
    struct super_block *d_sb; /* dentry树的根,超级块 */ 
    void *d_fsdata; /* 特定于文件系统的数据 */ 
    int d_mounted; 
    unsigned char d_iname[DNAME_INLINE_LEN_MIN]; /* 短文件名存储在这里 */ 
};

5.1 几个重要的成员变量说明

(1)dentry中的d_mounted

如果当前dentry对象表示一个装载点,那么d_mounted设置为1;否则其值为0。

5.2 dentry缓存

在VFS连同文件系统实现读取的一个目录项(目录或文件)的数据之后,则创建一个dentry实例,以缓存找到的数据。dentry缓存是如何组织的?dentry对象在内存中的组织,涉及下面两个部分:

  • 一个散列表(dentry_hashtable)包含了所有的dentry对象。
  • 一个LRU(最近最少使用,least recently used)链表,其中不再使用的对象将授予一个最后宽限期,宽限期过后才从内存移除。

5.2.1 dentry缓存相关的哈希散列表

内存中所有活动的dentry实例都保存在一个散列表中,该散列表使用fs/dcache.c中的全局变量dentry_hashtable实现。fs/dcache.c中的d_hash函数用于确定dentry对象的散列位置。

5.2.2 dentry缓存相关的链表

(1)struct list_head d_lru;

全局变量dentry_unused用作表头,该链表管理最近最少使用的dentry。

5.3 dentry相关的链表

5.3.1 相关成员变量

cpp 复制代码
struct dentry *d_parent; /* 父目录的dentry实例 */
struct list_head d_subdirs; /* 子目录/文件的目录项链表 */ 
struct list_head d_child; /* 链表元素,用于将当前dentry连接到父目录dentry的d_subdirs链表中 */

5.3.2 链表关系图示

相关推荐
A小辣椒1 天前
TShark:Wireshark CLI 功能
linux
A小辣椒1 天前
TShark:基础知识
linux
AlfredZhao1 天前
OCI 明明分配了 200G 系统盘,为什么 df 只看到 30G?
linux·oci
AlfredZhao2 天前
vi 删除指定范围的行,不用再反复按 dd
linux·vi
用户9718356334662 天前
银河麒麟 KY10 申威(SW64) 安装 nginx-1.16.1-2.p01.ky10.sw_64.rpm 详细步骤
linux
猪脚踏浪2 天前
linux 拷贝文件或目录到指定的位置
linux
摇滚侠3 天前
Linux CentOS7 rpm 安装 MySQL 5.7
linux·运维·mysql
bush43 天前
嵌入式linux学习记录十四、术语
linux·嵌入式
载数而行5203 天前
Linux 11 动态监控指令top
linux
不会C语言的男孩3 天前
Linux 系统编程 · 第 8 章:进程基础
linux·c语言