虚拟文件系统之VFS的数据结构(上)

努力成为linux kernel hacker的人李万鹏原创作品,为梦而战。转载请标明出处

http://blog.****.net/woshixingaaa/archive/2011/05/14/6420098.aspx

VFS所隐含的主要思想在于引入了一个通用的文件模型(common file model),这个模型能够表示所有支持的文件系统。Linux希望以最小的额外开销运行他的本地文件系统。不过,要实现每个具体的文件系统,必须将其物理组织结构转换为虚拟文件系统的通用模型。例如:在通用文件模型中,每个目录被看作一个文件,可以包含若干子目录和其他文件。但是,存在几个非Unix的基于磁盘的文件系统,他们利用文件分配表(File Allocation Table,FAT)存放每个文件在目录树中的位置,在这些文件系统中,存放的是目录而不是文件。为了符合VFS的通用文件模型,对上述基于FAT的文件系统的实现,Linux必须在必要时能够快速建立对应于目录的文件。这样的文件只作为内核内存的对象而存在。通用文件模型由下列对象类型组成:

  • 超级块对象(superblock object)
  • 索引节点对象(inode object)
  • 文件对象(file object)
  • 目录项对象(dentry object)

超级块对象(superblock object):

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; /*磁盘限额管理方法*/ const 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_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 list_head s_more_io; /* parked for more writeback */ struct hlist_head s_anon; /* anonymous dentries for (nfs) exporting */ /*用于处理远程网络文件系统的匿名目录项的链表*/ struct list_head s_files; /*文件对象的链表*/ /* s_dentry_lru and s_nr_dentry_unused are protected by dcache_lock */ struct list_head s_dentry_lru; /* unused dentry lru */ int s_nr_dentry_unused; /* # of dentry on lru */ struct block_device *s_bdev; struct mtd_info *s_mtd; 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 */ fmode_t s_mode; /* * 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; /* * Filesystem subtype. If non-empty the filesystem type field * in /proc/mounts will be "type.subtype" */ char *s_subtype; /* * Saved mount options for lazy filesystems using * generic_show_options() */ char *s_options; /* * storage for asynchronous operations */ struct list_head s_async_list; };

虚拟文件系统之VFS的数据结构(上)

虚拟文件系统之VFS的数据结构(上)

存放已安装文件系统的有关信息。对于基于磁盘的文件系统,这类对象通常对应于存放在磁盘上的文件系统控制块(filesystem control block)

superblock存在于两个链表中,一个是系统所有superblock的链表,一个是对于特定的文件系统的链表。对于系统所有superblock的链表,链表中的第一个元素用super_blocks变量来表示,而超级块对象的s_list字段存放指向链表相邻元素的指针。sb_lock自旋锁保护链表免受多处理器系统上的同时访问;对于特定的文件系统的链表,file_system_typefs_supers字段表示给定类型的已安装文件系统所对应的超级块链表的头。链表元素的向后和向前链接存放在超级块对象的s_instances字段中。

s_fs_info字段指向属于具体文件系统的超级块信息,比如Ext2文件系统,该字段就指向ext2_sb_info数据结构,该结构包括磁盘分配位掩码和其他与VFS的通用文件模型无关的数据。通常为了效率,由s_fs_info字段所指向的数据被复制到内存。任何基于磁盘的文件系统都需要访问和更改自己的磁盘分配位图,以遍分配和释放磁盘块。VFS允许这些文件系统直接对内存超级块的s_fs_info字段进行操作,而无需访问磁盘。但是,这种方法带来一个新的问题:有可能VFS超级块最终不再与磁盘上相应的超级块同步。因此有必要引入一个s_dirt标志来表示该超级块是否是脏的——那磁盘上的数据是否需要更新。缺乏同步还会导致我们熟悉的一个问题:当一台机器的电源突然断开而用户来不及正常关闭系统时,就会出现文件系统崩溃。

索引节点对象(inode object):

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; u64 i_version; loff_t i_size; #ifdef __NEED_I_SIZE_ORDERED seqcount_t i_size_seqcount; #endif struct timespec i_atime; struct timespec i_mtime; struct timespec i_ctime; unsigned int i_blkbits; blkcnt_t i_blocks; unsigned short i_bytes; umode_t i_mode; spinlock_t i_lock; /* i_blocks, i_bytes, maybe i_size */ struct mutex i_mutex; struct rw_semaphore i_alloc_sem; const struct inode_operations *i_op; const struct file_operations *i_fop; /* former ->i_op->default_file_ops */ struct super_block *i_sb; struct file_lock *i_flock; struct address_space *i_mapping; 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; }; int i_cindex; __u32 i_generation; #ifdef CONFIG_DNOTIFY unsigned long i_dnotify_mask; /* Directory notify events */ struct dnotify_struct *i_dnotify; /* for directory notifications */ #endif #ifdef CONFIG_INOTIFY struct list_head inotify_watches; /* watches on this inode */ struct mutex inotify_mutex; /* protects the watches list */ #endif unsigned long i_state; unsigned long dirtied_when; /* jiffies of first dirtying */ unsigned int i_flags; atomic_t i_writecount; #ifdef CONFIG_SECURITY void *i_security; #endif void *i_private; /* fs or device private pointer */ };

虚拟文件系统之VFS的数据结构(上)

存放关于具体文件的一般信息。对基于磁盘的文件系统,这类对象通常对应于存放在磁盘上的文件控制块(file control block)。每个索引节点对象都有一个索引节点号,这个节点号唯一地标识文件系统中的文件。文件系统处理文件所需要的所有信息都放在一个名为索引节点的数据结构中。文件名可以随时更改,但是索引节点对文件是唯一的,并且随文件的存在而存在。内存中的索引节点对象由一个inode数据结构组成。

inode存在于两个循环双向链表和一个散列表中。一个双向循环链表是inode所在文件系统的super blocks_inodes链表中。链表的头部存放在超级块对象的s_inodes字段中;索引节点对象的i_sb_list字段存放了指向链表相邻元素的指针。另一个循环链表是根据inode的使用状态处于以下3个链表中的某一个:

  1. 有效未使用的索引节点链表
  2. 正在使用的索引节点链表

  3. 脏索引节点链表

现在来看这3个链表:

1.有效未使用的索引节点链表,典型的如那些镜像有效的磁盘索引节点,且当前未被任何进程使用。这些节点不为脏,且它们的i_count字段置为0。链表中的首元素和尾元素是由变量inode_unusednext字段和prev字段分别指向的。这个链表用作磁盘高速缓存。

2.正在使用的索引节点链表,也就是那些镜像有效的磁盘索引节点,且当前被某些进程使用。这些索引节点不为脏,但他们的i_count字段为正数。链表中的首元素和尾元素是由变量inode_in_use引用的。

3.脏索引节点的链表。链表中的首元素和尾元素是由相应超级块对象的s_dirty字段引用的。这些链表都是通过适当的索引节点对象的i_list字段链接在一起的。

最后,索引节点对象也存放在一个称为inode_hashtable的散列表中。散列表加快了对索引节点对象的搜索,前提是系统内核要知道索引节点号及文件所在文件系统对应的超级块对象的地址。由于散列技术可能引发冲突,所以索引节点对象包含一个i_hash字段,该字段包含向前和向后两个指针,分别指向散列到同一地址的前一个索引节点和后一个索引节点;该字段因此创建了由这些索引节点组成的双向链表。