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 链表关系图示

相关推荐
真真-真真10 分钟前
WebXR
linux·运维·服务器
轩辰~32 分钟前
网络协议入门
linux·服务器·开发语言·网络·arm开发·c++·网络协议
雨中rain1 小时前
Linux -- 从抢票逻辑理解线程互斥
linux·运维·c++
Bessssss2 小时前
centos日志管理,xiao整理
linux·运维·centos
s_yellowfish2 小时前
Linux服务器pm2 运行chatgpt-on-wechat,搭建微信群ai机器人
linux·服务器·chatgpt
豆是浪个2 小时前
Linux(Centos 7.6)yum源配置
linux·运维·centos
vvw&2 小时前
如何在 Ubuntu 22.04 上安装 Ansible 教程
linux·运维·服务器·ubuntu·开源·ansible·devops
我一定会有钱2 小时前
【linux】NFS实验
linux·服务器
Ven%2 小时前
如何在防火墙上指定ip访问服务器上任何端口呢
linux·服务器·网络·深度学习·tcp/ip
是阿建吖!2 小时前
【Linux】基础IO(磁盘文件)
linux·服务器·数据库