赞
踩
1 VFS 文件系统
计算机体系由三个部分组成:计算,存储,网络,体现了物体流转的三个方面。
生产酒也是这样的一个过程,首先是收割小麦,将小麦发酵蒸馏形成了酒(处理),然后装在瓶子里,运送到仓库(传输)。最后将所有的酒存在仓库里(存储)
物理设备比如磁盘可以看作是土地,有了土地之后并不能使用,它还无法存储酒。需要在土地上建立仓库,并给每一个仓库通电,编号。这个过程可以理解为建立文件系统的过程。文件系统建立以后就可以往里面存酒了。什么样的酒存在哪个仓库需要有管理人员记录在本子上。当然仓库在存储的时候不一定存的都是酒,也可能存一些箱子,或者是小麦。
虚拟文件系统可以理解为建造仓库的标准。建造仓库的时候我们会告诉施工队,我要实现的功能有哪些,比如我希望仓库可以存储5吨的货物,仓库要有一个自动门,汽车来到验证车牌可以放行,仓库内部的温度要保存在5度左右。只要满足这几个要求和标准即可。至于施工队用什么样的材料建造房屋。用的是铁门还是合金门,都不是使用仓库的人要考虑的。
而在VFS则定义了虚拟文件系统,这个东西类似于建造仓库的标准。有了标准之后,所有的文件系统都需要按照这种标准来构建文件系统。
从上图可以看到,虚拟机文件系统介于具体的文件系统和C语言标准库之间,有了VFS之后,所有按照VFS接口开发的文件系统都可以接入linux,标准的接口提供了标准的操作,接口的设计理念一致贯穿于整个编程科学的发展。
2 VFS关键数据结构及关系
上面是虚拟文件系统的一个概览,虚拟文件系统定义了通用接口,所有具体的文件系统都必须实现这样的结构。task_struct是进程描述符,里面记录了进程的相关信息。一个进程可能操作某个文件,因此,task_struck成员内部有一个files,filtes对应files_struct里面记录了所有打开的file,file于file之间通过双向链表组织起来。
file内部包含了d_dentry,也就是目录项,通过目录项可以找到对应的inode,inode记录了文件的权限和属性。进程在操作文件的时候需要用到权限和属性,比如判断某个进程对某个文件是不是有写权限。inode里面有一个成员i_mapping,对应address_space。其中的i_mapping域指向一个address_space结构。这样,一个文件就对应一个address_space结构,一个 address_space与一个偏移量能够确定一个page cache 或swap cache中的一个页面。至于什么是page cache和swap cache可以
查阅相关知识。以上就是VFS组件相互之间的关系,下面看看每一个具体的组件。
super_block
是文件系统的心脏,它存储了文件系统的全局信息,如硬盘已用空间,数据块可用空间。
文件系统所有的inode信息都链接到超级块上, 如下所示是超级块的数据结构,下面节选某些关键信息。
struct super_block { struct list_head s_list; /* Keep this first */ dev_t s_dev; /* search index; _not_ kdev_t */ unsigned char s_blocksize_bits; unsigned long s_blocksize; loff_t s_maxbytes; /* Max file size */ struct file_system_type *s_type; ……….. unsigned int s_quota_types; /* Bitmask of supported quota types */ struct quota_info s_dquot; /* Diskquota specific options */ struct sb_writers s_writers; void *s_fs_info; /* Filesystem private info */ /* Granularity of c/m/atime in ns (cannot be worse than a second) */ u32 s_time_gran; #ifdef CONFIG_FSNOTIFY __u32 s_fsnotify_mask; struct fsnotify_mark_connector __rcu *s_fsnotify_marks; #endif char s_id[32]; /* Informational name */ uuid_t s_uuid; /* UUID */ unsigned int s_max_links; fmode_t s_mode; struct mutex s_vfs_rename_mutex; /* Kludge */ char *s_subtype; const struct dentry_operations *s_d_op; /* default d_op for dentries */ struct user_namespace *s_user_ns; struct list_head s_inodes; /* all inodes */ spinlock_t s_inode_wblist_lock; struct list_head s_inodes_wb; /* writeback inodes */ }
s_blocksize 代表文件系统的大小。
s_inodes 代表了所有的inode。
s_type 代表文件系统的类型。
Inode
代表一个具体的文件, 包含文件的大小,创建时间,文件的块大小等数据,以及对文件的读写函数,文件的读写缓存信息等。看一下inode的数据结构,这里只保留一些关键的数据信息,其他一些都去掉了。
struct inode { umode_t i_mode; unsigned short i_opflags; kuid_t i_uid; kgid_t i_gid; unsigned int i_flags; const struct inode_operations *i_op; struct super_block *i_sb; struct address_space *i_mapping; unsigned long i_ino; { const unsigned int i_nlink; unsigned int __i_nlink; }; /* 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 */ struct list_head i_lru; /* inode LRU list */ struct list_head i_sb_list; struct list_head i_wb_list; /* backing dev writeback list */ const struct file_operations *i_fop; /* former ->i_op->default_file_ops */ 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; void *i_private; /* fs or device private pointer */ 。。。。。。。 }
链表其实是linux中组织数据非常常用的数据结构,这个结构里面有三个链表头。
i_mapping是非常重要的一个结构,描述了文件地址。
block_device描述对应的块设备(块设备可以被看作一个文件系统来处理)。
inode_operations,该结构体定义了对文件的操作(如下代码所示),可以看到有获取属性,设置属性,重新命名等操作
i_mode,主要作用是用来区分文件类型,比如块设备,字符设备,目录, socket等。
super_block,指向超级块,也就是表示文件系统基本信息的数据结构。
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); }
【文章福利】小编推荐自己的Linux内核技术交流群: 【977878001】整理一些个人觉得比较好得学习书籍、视频资料!进群私聊管理领取 内核资料包(含视频教程、电子书、实战项目及代码)
内核资料直通车:Linux内核源码技术学习路线+视频教程代码资料
学习直通车:Linux内核源码/内存调优/文件系统/进程管理/设备驱动/网络协议栈
dentry
目录项对象,代表一个目录项。目录项反应了文件系统的树状结构,目前主流的操作系统基本都是用树状结构来组织文件的。linux也不例外。dentry表示一个目录项,目录项下面又有子目录。
struct dentry { /* RCU lookup touched fields */ unsigned int d_flags; /* protected by d_lock */ seqcount_t d_seq; /* per dentry seqlock */ struct hlist_bl_node d_hash; /* lookup hash list */ struct dentry *d_parent; /* parent directory */ struct qstr d_name; struct inode *d_inode; /* Where the name belongs to - NULL is * negative */ unsigned char d_iname[DNAME_INLINE_LEN]; /* small names */ /* Ref lookup also touches following */ struct lockref d_lockref; /* per-dentry lock and refcount */ const struct dentry_operations *d_op; struct super_block *d_sb; /* The root of the dentry tree */ unsigned long d_time; /* used by d_revalidate */ void *d_fsdata; /* fs-specific data */ union { struct list_head d_lru; /* LRU list */ wait_queue_head_t *d_wait; /* in-lookup ones only */ }; struct list_head d_child; /* child of parent list */ struct list_head d_subdirs; /* our children */ /* * d_alias and d_rcu can share memory */ union { struct hlist_node d_alias; /* inode alias list */ struct hlist_bl_node d_in_lookup_hash; /* only for in-lookup ones */ struct rcu_head d_rcu; } d_u; }
d_inode 这个成员志向inode,
super_block 指向对应的超级块。
dentry_operations 这个成员指向dentry对应的操作,如下所示
struct dentry_operations { int (*d_revalidate)(struct dentry *, unsigned int); int (*d_weak_revalidate)(struct dentry *, unsigned int); int (*d_hash)(const struct dentry *, struct qstr *); int (*d_compare)(const struct dentry *, unsigned int, const char *, const struct qstr *); int (*d_delete)(const struct dentry *); int (*d_init)(struct dentry *); void (*d_release)(struct dentry *); void (*d_prune)(struct dentry *); void (*d_iput)(struct dentry *, struct inode *); char *(*d_dname)(struct dentry *, char *, int); struct vfsmount *(*d_automount)(struct path *); int (*d_manage)(const struct path *, bool); struct dentry *(*d_real)(struct dentry *, const struct inode *); }
File
文件对象,代表进程打开的文件,描述的是进程和文件之间的关系。一个文件,在不同的进程中有不同的文件对象,如下图是文件的数据结构
从文件操作的角度来看文件系统不同结构体之间的关系。
文件系统的主要目的是解决用户对文件的操作,包括创建,删除,修改,存放。操作文件需要在内核中创建相对应的进程,有了进程之后,需要知道进程操作的文件是什么。那么反应进程和文件之间关系的就是file。file是实际发生操作时创建出来的对象,存储在内存里。file内部保存着文件的dentry,也就是目录项,除此之外还保存着file_operation,也就是对文件的读写,打开等操作。通过dentry还可以找到inode,那么就可以对inode执行mkdir等操作。super_block代表了一个文件系统,以及文件了解一个文件系统所需的基本数据。
参考资料:
《深入linux内核架构》
《linux内核探秘》
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。