每个VFS对象都存放在一个适当的数据结构中,其中包括对象的属性和指定对象方法表的指针。内核可以动态地修改对象的方法,因此可以为对象建立专用的行为。下面我们来具体的看一下VFS中的数据结构。 超级块对象(super_block)
超级块用来描述整个文件系统的信息。对每个具体的文件系统来说,都有各自的超级块,如Ext2超级块,它们存放于磁盘上。当内核在对一个文件系统进行初始化和注册时在内存为其分配一个超级块,这就是VFS超级块。也就是说,VFS超级块是在文件系统安装时建立的,并在这些文件系统卸载时被自动删除,可见,VFS超级块只存在于内存中。看一下超级块在内核中的数据:
- struct super_block {
- struct list_head s_list; /* Keep this first */
- dev_t s_dev; /* search index; _not_ kdev_t */
- unsigned char s_dirt;
- unsigned char s_blocksize_bits;
- unsigned long s_blocksize;
- loff_t s_maxbytes; /* Max file size */
- struct file_system_type *s_type;
- const struct super_operations *s_op;
- const struct dquot_operations *dq_op;
- const 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;
- atomic_t s_active;
- struct list_head s_inodes;
- struct list_head files;
- struct list_head s_instances;
- ......
- }
s_list; 指向超级块的指针,超级块是个双向循环链表,通常我们通过list_entry宏来获取s_list所在超级块结构体的地址。超级块链表的头结点是变量super_blocks,sb_lock自旋锁保护链表免受多处理器系统上的同时访问。
s_dev; 具体文件系统的块设备标志符,比如,ext2文件系统所在设备为磁盘,则该设备号即为该磁盘在系统中的设备号。
s_dirt; 修改脏标识,超级块在内存中被修改后,该标志为1,则修改后的超级块必须写回磁盘.
s_blocksize_bits; 以位为单位的块大小。例如,如果块大小为1024字节,则该值为10.
s_blocksize; 以字节为单位的块大小,该值即代表这个块的具体大小.
s_maxbytes; 文件的最长长度。
*s_type; 指向具体文件系统的文件系统类型。
*s_op; 超级块方法。
*dq_op; 磁盘限额处理方法。
*s_qcop; 磁盘限额管理方法。
*s_export_op;网络文件系统使用的输出操作。
s_flags; 安装文件系统时的标志,记录比如只读或可读可写等这样的标志。
s_root:该文件系统根目录的目录项结构指针。利用该根目录项,可以访问到这个文件系统中的任何一个文件。
s_count:对该超级块的引用计数;
s_lock: 超级块信号量;
s_inodes:该文件系统中所有的索引结点形成一个双链表,该字段存放这个链表的头结点;
s_files:该文件系统中所有已被打开的文件形成一个双链表,该字段存放这个链表的头结点;
s_instances:某个具体文件系统中所有超级块会组成一个双链表。这个链表的头结点为super_block,头结点定义在该文件系统对应的file_system_type结构体中;
s_id[32]:文件系统的名称。比如ext3文件系统,该值为“ext3”;
与超级块关联的方法就是超级块操作,这些操作是super_operations来描述的,该结构的起始地址存放在超级块的s_op字段中。
- struct super_operations {
- struct inode *(*alloc_inode)(struct super_block *sb);
- void (*destroy_inode)(struct inode *);
- void (*dirty_inode) (struct inode *, int flags);
- int (*write_inode) (struct inode *, struct writeback_control *wbc);
- int (*drop_inode) (struct inode *);
- void (*evict_inode) (struct inode *);
- void (*put_super) (struct super_block *);
- void (*write_super) (struct super_block *);
- ......
- }
alloc_inode(sb):为索引结点分配空间,包括具体文件系统的的数据所需要的空间。
destroy_inode(inode):释放指定的索引结点;
write_inode(inode,flag):将指定的inode写回磁盘,用于指定inode的更新;flag参数表示I/O操作是否同步;
drop_inode(inode):移走指定的inode,与write_inode成对出现;
delete_inode(inode) : 在必须撤销索引结点时调用,删除内存中的VFS索引结点和磁盘上的文件数据及元数据。
put_super(super):释放指定的超级块,文件系统被卸载时使用;
write_super(super):如果该超级块被修改,即s_dirt为1时,则要将超级块写回磁盘,同时还要将s_dirt重设为0;
当文件系统需要对其所对应的超级块进行操作时,就应该使用超级块操作类中的具体函数。比如,定义sb为指向某个超级块的指针,如果该超级块需要将自己写回磁盘,则这样调用:sb->s_op->write_super(sb);
可以看到,虽然write_super函数是由sb所指的超级块所调用的,但是仍然将sb传递给write_super函数。
索引节点对象:
文件系统处理文件所需要的所有信息都存放在称为索引结点的 数据结构中,文件名可以随时更改,但是索引结点对文件是唯一的,并且随文件的存在而存在。具体文件系统的索引结点是存放在磁盘上的,是一种静态结构,要使用它。必须调用内存,填写VFS的索引结点。VFS的主要域定义如下:
- struct inode {
- struct hlist_node i_hash;
- struct list_head i_list; /* backing dev IO 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;
- struct timespec i_atime;
- struct timespec i_mtime;
- struct timespec i_ctime;
- blkcnt_t i_blocks;
- unsigned short i_bytes;
- umode_t i_mode;
- const struct inode_operations *i_op;
- const struct file_operations *i_fop; /* former ->i_op->default_file_ops */
- struct list_head i_devices;
- union {
- struct pipe_inode_info *i_pipe;
- struct block_device *i_bdev;
- };
- 。。。。。
- };
i_hash:为了提高查找正在被使用的inode的效率,每一个inode都会有一个hash值,所有hash值相同的inode形成一个双链表。该字段包含prev和next两个指针,分别指向上述链表的前一个元素和后一个元素;
i_list:VFS中使用四个链表来管理不同状态的inode结点。inode_unused将当前未使用的inode链接起来,inode_in_use将当前正在被使用的inode链接起来,超级块中的s_dirty将所有脏inode链接起来,i_hash将所有hash值相同的inode链接起来。i_list中包含prev和next两个指针,分别指向与当前inode处于同一个状态链表的前后两个元素。
i_sb_list:每个文件系统中的inode都会形成一个双联表,这个双链表的头结点存放在超级块的s_inodes中。而该字段中的prev和next指针分别指向在双链表中与其相邻的前后两个元素;
i_dentry:所有引用该inode的目录项将形成一个双联表,该字段即为这个双联表的头结点;
i_ino:索引结点号。通过ls -l命令可以查看文件的索引节点号;
i_count:引用计数;
i_nlink:硬链接数。当该inode描述一个目录时,这个值至少为2,代表.和..的数目;
i_uid:inode所属文件的拥有者的id,通过ls -n可查看拥有者id;
i_gid:inode所属文件所在组的id,通过ls -n可查看组id;
i_rdev:如果该inode描述的是一个设备文件,此值为设备号;
i_blkbits:以位为单位的块大小;
i_atime:文件最近一次被访问的时间。通过ls -lu可查看该时间;
i_mtime:文件最近一次被修改的时间,这里的修改只文件内容被修改。通过ls -l可查看该时间;
i_ctime:文件最近一次被修改的时间,这里的修改除了指文件内容被修改外,更强调的是文件的属性被修改。通过ls -lc可查看该时间;
i_blocks:文件使用块的个数,通过ls -s可以查看该某个文件的块使用数目;
i_mode:文件的访问权限;
i_op:指向索引结点操作结构体的指针;
i_fop:指向文件操作结构体的指针,这个字段用来初始化文件结构体(struct file)中的f_op字段;
i_sb:指向inode所属文件系统的超级块的指针;
i_pipe:如果inode所代表的文件是一个管道,则使用该字段;
i_bdev:如果inode所代表的文件是一个块设备,则使用该字段;
i_cdev:如果inode所代表的文件是一个字符设备,则使用该字段;
每个索引结点对象总是出现在下列双向循环链表的某个链表中(所有情况下,指向相邻元素的指针存放在i_list字段中):有效未使用的索引结点链表、正在使用的索引结点链表、脏索引结点的链表。
与索引结点对象相关联的方法叫索引结点操作:
- struct inode_operations {
- struct dentry * (*lookup) (struct inode *,struct dentry *, struct nameidata *);
- int (*create) (struct inode *,struct dentry *,int, struct nameidata *);
- 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 *,int);
- int (*rmdir) (struct inode *,struct dentry *);
- }
create():创建一个新的磁盘索引结点。
lookup():查诈一个索引结点所在的目录。
link():创建一个新的硬链接。
unlink():删除一个硬链接。
symlink():为符号链接创建一个新的索引结点。
mkdir():为目录项创建一个新的索引结点。
阅读(2744) | 评论(0) | 转发(3) |