- VFS对象及其数据结构
VFS其实采用的是面向对象的思想,使用一族数据结构来代表通用文件对象。这些数据结构表现得就像是对象。因为内核纯粹使用C代码实现,没有直接利用面向对象的语义,所以内核中的数据结构都使用C结构体实现,但这些结构体包含数据的同时也包含操作这些数据的函数指针,其中的操作函数由具体文件系统实现。
VFS中有四个主要的对象类型,它们分别是:
1. 超级块对象,它代表一个已安装文件系统
2. 索引节点对象,它代表一个文件
3. 目录项对象,它代表一个目录项,是路径的一个组成部分
4. 文件对象,它代表由进程打开的文件
注意,因为VFS将目录作为一个文件来处理,所以不存在目录对象。目录项代表的是路径中的一个组成部分,它可能包括一个普通文件。换句话说,目录项不同于目录,但目录却和文件相同。
每个主要对象中都包含一个操作对象,这些操作对象描述了内核针对主要对象可以使用的方法。最主要的几种操作对象如下:
1. super_operations对象,其中包括内核针对特定文件系统所能调用的方法,比如read_inode()和sync_fs()等方法。
2. inode_operations对象,其中包括内核针对特定的文件所能调用的方法,比如create()和link()等方法。
3. dentry_operations对象,其中包括内核针对特定目录所能调用的方法,比如d_compare()和d_delete()等方法。
4. file对象,其中,包括进程针对已打开文件所能调用的方法,比如read()和write()等方法。
操作对象作为一个指针结构体被实现,此结构体中包含指向操作其父对象的函数指针。对于其中许多方法来说,可以继承使用VFS提供的通用函数,如果通用函数提供的基本功能无法满足需要,就不许使用实际文件系统的独有方法填充这些函数指针,使其指向文件系统实例。
注意,这里所说的对象指的是结构体,不是像C++和Java那样的真正的数据类型。
VFS使用了大量结构体对象,它所包括的对象远远多于上述几种对象,比如每个注册的文件系统都由file_system_type结构体表还是,它描述了文件系统及其能;另外,每一个安装点也都用vfsmount结构体表示,它保护的是安装点的相关信息,如位置和安装标志等。三个与进程相关的结构体分别是:file_struct,fs_struct和namespace。它们描述了文件系统以及和进程相关的文件。
- 超级块对象
各种文件系统都必须实现超级块,该对象用于存储特定文件系统的信息,通常对应于存放在磁盘特定扇区中(所以叫超级块对象)的文件系统超级块或文件替他控制块。对于并非基于磁盘的文件系统(如基于内存的文件系统,比如sysfs),它们会在使用现场创建超级块并将其保存到内存中。
超级块对象由super_block结构体表示:
- 在<Fs.h(include/linux)>中
- struct super_block {
- struct list_head s_list; /* Keep this first */
- dev_t s_dev; /* search index; _not_ kdev_t */
- unsigned long s_blocksize; /* 以字节为单位的块大小 */
- unsigned char s_blocksize_bits; /* 以位为单位的块大小 */
- unsigned char s_dirt; /* 修改(脏)标志*/
- unsigned long long s_maxbytes; /* Max file size */
- struct file_system_type *s_type; /*文件系统类型 */
- const struct super_operations *s_op; /* 超级块方法 */
- struct dquot_operations *dq_op; /* 磁盘限额方法 */
- struct quotactl_ops *s_qcop; /*限额控制方法 */
- struct export_operations *s_export_op; /* 导出方法 */
- unsigned long s_flags; /* 登录标志 */
- unsigned long s_magic; /* 文件系统的魔数 */
- struct dentry *s_root; /* 目录登录点 */
- struct rw_semaphore s_umount; /* 卸载信号量 */
- struct mutex s_lock; /* 超级块互斥锁 */
- int s_count; /* 超级块引用计数 */
- int s_syncing; /* 文件系统同步标志 */
- int s_need_sync_fs; /* 尚未同步标志 */
- atomic_t s_active; /* 活动引用计数 */
- #ifdef CONFIG_SECURITY
- void *s_security; /* 安全模块 */
- #endif
- struct xattr_handler **s_xattr; /* */
- struct list_head s_inodes; /* all inodes */
- struct list_head s_dirty; /* dirty inodes */
- struct list_head s_io; /* parked for writeback */
- struct hlist_head s_anon; /* anonymous dentries for (nfs) exporting */
- struct list_head s_files; /* 被分配文件链表 */
- struct block_device *s_bdev; /* 相关块设备 */
- struct list_head s_instances; /* 该类型的文件系统 */
- struct quota_info s_dquot; /* Diskquota specific options */
- int s_frozen; /* */
- wait_queue_head_t s_wait_unfrozen;
- char s_id[32]; /* Informational name */
- void *s_fs_info; /* Filesystem private info */
- /*
- * The next field is for VFS *only*. No filesystems have any business
- * even looking at it. You had been warned.
- */
- struct mutex s_vfs_rename_mutex; /* Kludge */
- /* Granularity of c/m/atime in ns.
- Cannot be worse than a second */
- u32 s_time_gran;
- };
创建、管理和销毁超级块对象的代码位于文件fs/super.c中。超级块对象通过alloc_super()函数创建并初始化。在文件系统安装时,内核会调用该函数以便从磁盘读取文件系统超级块,并且将其信息填充到内存中的超级块对象中。
- /**
- * alloc_super - create new superblock
- * @type: filesystem type superblock should belong to
- *
- * Allocates and initializes a new &struct super_block. alloc_super()
- * returns a pointer new superblock or %NULL if allocation had failed.
- */
- static struct super_block *alloc_super(struct file_system_type *type)
- {
- struct super_block *s = kzalloc(sizeof(struct super_block), GFP_USER);
- static struct super_operations default_op;
- if (s) {
- if (security_sb_alloc(s)) {
- kfree(s);
- s = NULL;
- goto out;
- }
- INIT_LIST_HEAD(&s->s_dirty);
- INIT_LIST_HEAD(&s->s_io);
- INIT_LIST_HEAD(&s->s_files);
- INIT_LIST_HEAD(&s->s_instances);
- INIT_HLIST_HEAD(&s->s_anon);
- INIT_LIST_HEAD(&s->s_inodes);
- init_rwsem(&s->s_umount);
- mutex_init(&s->s_lock);
- lockdep_set_class(&s->s_umount, &type->s_umount_key);
- /*
- * The locking rules for s_lock are up to the
- * filesystem. For example ext3fs has different
- * lock ordering than usbfs:
- */
- lockdep_set_class(&s->s_lock, &type->s_lock_key);
- down_write(&s->s_umount);
- s->s_count = S_BIAS;
- atomic_set(&s->s_active, 1);
- mutex_init(&s->s_vfs_rename_mutex);
- mutex_init(&s->s_dquot.dqio_mutex);
- mutex_init(&s->s_dquot.dqonoff_mutex);
- init_rwsem(&s->s_dquot.dqptr_sem);
- init_waitqueue_head(&s->s_wait_unfrozen);
- s->s_maxbytes = MAX_NON_LFS;
- s->dq_op = sb_dquot_ops;
- s->s_qcop = sb_quotactl_ops;
- s->s_op = &default_op;
- s->s_time_gran = 1000000000;
- }
- out:
- return s;
- }
超级块操作
超级块对象中最重要的一个域是s_op,它指向超级块的操作函数表。超级块操作函数表由super_operations结构标示:
- /*
- * NOTE: write_inode, delete_inode, clear_inode, put_inode can be called
- * without the big kernel lock held in all filesystems.
- */
- struct super_operations {
- /* 该方法在给定的超级块下创建一个新的索引节点对象 */
- struct inode *(*alloc_inode)(struct super_block *sb);
- /* 该函数用于释放给定的索引节点 */
- void (*destroy_inode)(struct inode *);
- /* 该函数以inode->i_ino为索引。从磁盘上读取索引节点,并填充内存中对应的索引节点结构的剩余部分 */
- void (*read_inode) (struct inode *);
- /* VFS在索引节点脏(被修改)时会调用此函数 。日志文件系统(如ext3)执行该函数进行日志更新*/
- void (*dirty_inode) (struct inode *);
- /* 该函数用于将给定的索引节点写入磁盘 */
- int (*write_inode) (struct inode *, int);
- /* 该函数用于释放给定的索引节点 */
- void (*put_inode) (struct inode *);
- /* 在最后一个指向索引节点的引用被释放后,VFS会调用该函数。VFS只需要简单地删除这个索引节点后,普通Unix文件系统就不定义这个函数。注意调用者必须持有inode_lock锁 */
- void (*drop_inode) (struct inode *);
- /* 该函数用于从磁盘上释放给定的索引节点 */
- void (*delete_inode) (struct inode *);
- /* 该函数在卸载文件系统时由VFS调用,用来释放超级块 */
- void (*put_super) (struct super_block *);
- /* 该函数用给定的超级块跟新磁盘上的超级块。VFS通过该函数对内存中的内存中的超级块和磁盘折哦你哦个的超级块进行同步 */
- void (*write_super) (struct super_block *);
- /* 该函数使文件系统的数据元与磁盘上的文件系统同步。wait参数指定操作是否同步*/
- int (*sync_fs)(struct super_block *sb, int wait);
- /* 该函数首先禁止对文件系统做改变,再使用给定的超级块更新磁盘上的超级块。目前LVM(逻辑卷标管理)会调用该函数 */
- void (*write_super_lockfs) (struct super_block *);
- /* 该函数对文件系统解除锁定,它是write_super_lockfs()的逆操作*/
- void (*unlockfs) (struct super_block *);
- /* VFS通过调用该函数获取文件系统状态。指定文件系统相关的统计信息将放置在kstatfs结构中 */
- int (*statfs) (struct dentry *, struct kstatfs *);
- /*当指定新的安装选项重新安装文件系统时,VFS会调用该函数*/
- int (*remount_fs) (struct super_block *, int *, char *);
- /* VFS调用该函数释放索引节点,并清空包含相关数据的所有页面*/
- void (*clear_inode) (struct inode *);
- /* VFS调用该函数中断安装操作。该函数被网络文件系统使用,如NFS */
- void (*umount_begin) (struct vfsmount *, int);
- int (*show_options)(struct seq_file *, struct vfsmount *);
- int (*show_stats)(struct seq_file *, struct vfsmount *);
- #ifdef CONFIG_QUOTA
- ssize_t (*quota_read)(struct super_block *, int, char *, size_t, loff_t);
- ssize_t (*quota_write)(struct super_block *, int, const char *, size_t, loff_t);
- #endif
- };
该结构的每一项都是一个指向超级块操作函数的指针,超级块操作函数执行文件系统和索引节点的低层操作。当文件系统需要对其超级块执行操作时,首先要在超级块对象中寻找需要的操作方法。所有以上函数都是由VFS在进程上下文中调用。必要时,它们都可以阻塞。其中的一些函数是可选的:在超级块操作表中,文件系统可以将不需要的函数指针设置成NULL。