风雨兼程

ring my bells
posts - 49, comments - 14, trackbacks - 0, articles - 0

Linux虚拟文件系统概述

Posted on 2008-04-16 21:18 silentneil 阅读(427) 评论(0)  编辑 收藏 引用
一、简介:
虚拟文件系统(也叫虚拟文件系统交换层)是在内核实现的一个软件层,它既为用户空间程序提供文件系统接口的调用,也是保证系统内核各种文件系统实现能够共存的抽象层。
VFS系统调用如open(2), stat(2), read(2), write(2), chmod(2)等在进程上下文中被调用。文件系统锁在文档Documentation/filesystems/Locking中描述。
1、目录结构缓存(dcache)
VFS实现了open(2), stat(2), chmod(2)以及其它类似的系统调用。VFS利用传递给这些调用的文件路径参数在目录入口缓存(dcache 或者目录机构缓存)中进行查找。通过一种快速的转换机制将文件路径转化为特定的目录入口。目录结构缓存只存在在RAM中而从不写入磁盘,他们只是为了提高性能而创建的。
目录结构缓存是整个文件空间的视图,但是绝大多数计算机并没有足够的空间将所有的目录结构同时放在RAM中,因此某些缓存将会丢弃。为了解决目录结构中的路径,VFS也许需要按照路径结构重新创建该缓存,并加载inode节点。这通过查找inode节点实现。
2、Inode节点对象
一个独立的目录结构通常会有一个指向一个inode的指针。Inodes是文件系统的对象,例如普通文件,目录,FIFO等。他们或者存在于磁盘上(快设备文件系统)或者内存中(伪文件系统)。被请求访问的inode从磁盘加载道内存,在修改后再写入磁盘。
一个独立的inode可以同时作为多个目录结构的指针目标(如硬链接时会出现这种情况)。
查找一个inode时,需要通过VFS对inode的父目录inode调用looup()方法。该方法在inode所属的具体文件系统中实现。一旦VFS 请求了目录结构(同时inode),我们就可以通过open(2)打开文件,或者stat(2)查看inode数据。stat(2)操作很简单,一旦 VFS获得了目录结构,它就察看inode数据,并将其中某些数据传给用户空间。
3、文件对象
打开一个文件还需要其它的操作:获取文件结构(内核实现的文件描述表)。最新分配的文件结构被初始化为一个指向目录结构的指针和一组文件操作函数集合。这些数据从inode获得。然后调用特定的文件系统实现的open()文件操作执行相关的任务。这是VFS交换功能的一个实现。文件结构被加入进程的文件表述表。
读、写和关闭文件(以及其它相关的VFS操作)通过用户空间的文件描述表获取对应的文件结构,然后调用请求的文件结构来实现需要得功能。一旦文件被打开,目录结构将保持在使用状态,表示VFS inode处于被访问状态。

二、注册和加载文件系统
注册和注销一个文件系统时,使用下面的API:
   #include <linux/fs.h>
   extern int register_filesystem(struct file_system_type *);
   extern int unregister_filesystem(struct file_system_type *);
传递的参数file_system_type结构描述了注册的文件系统。当生成将一个设备挂载到文件空间的一个目录时,VFS将调用相应文件系统所实现的get_sb()方法。接着,被挂载点的目录结构将被更新指向新文件系统的root inode节点。
在/proc/filesystems中可以看到在内核中注册的所有文件系统。
1、file_system_type结构
该结构对文件系统进行描述,在2.6.20内核中,其在include/linux/fs.h定义如下:
struct file_system_type {
const char *name;
int fs_flags;
int (*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;
struct lock_class_key s_lock_key;
struct lock_class_key s_umount_key;
};
name:   文件系统类型名,如"ext2", "iso9660", "msdos"等
fs_flags: 几种文件系统flag标志(FS_REQUIRES_DEV, FS_NO_DCACHE等)
get_sb:  加载(mount)新的文件系统实例时会调用的方法。sb指super block
kill_sb: 卸载(unmount)文件系统实例时调用
owner:  VFS内部使用,大多数情况下,应该被初始化为 THIS_MODULE
next:  VFS内部使用,初始化为NULL
s_lock_key:
s_umount_key:
get_sb参数如下:
struct super_block *sb: superblock结构,该结构部分由vFS初始化,其余的在get_sb()方法中初始化
int flags:  挂载标志
const char *dev_name: 挂载的设备名
void *data:  挂载属性选项,通常为ASCII字符串
int silent:  出错时的处理方式
get_sb()方法必须superblock中指定的块设备是否包含该方法支持的文件系统。执行成功,就返回指向superblock的指针,出错则返回NULL
s_op成员是get_sb中需要关注的项,它是一个指向结构"struct super_operations"的指针,该结构描述了文件系统的底层实现。
通常,一个文件系统使用get_sb()的具体实现中的一个,并提供fill_super方法。具体方法如下:
get_sb_bdev: 挂载块设备上的文件系统
get_sb_nodev:挂载无设备文件系统
get_sb_single:挂载在挂载点之间共享实例的文件系统
fill_super方法实现含有的变量如下:
struct super_block *sb: super_block结构,由fill_super()初始化
void *data:  挂载属性选项,通常为ASCII字符串
int silent:  出错时的处理方式

三、Superblock对象
一个Superblock对象代表了一个挂载的文件系统。
1、super_operations结构
该结构描述了VFS操作文件系统的方式。在2.6.20内核中,其在include/linux/fs.h定义如下:
struct super_operations {
    struct inode *(*alloc_inode)(struct super_block *sb);
void (*destroy_inode)(struct inode *);
void (*read_inode) (struct inode *);
 
    void (*dirty_inode) (struct inode *);
int (*write_inode) (struct inode *, int);
void (*put_inode) (struct inode *);
void (*drop_inode) (struct inode *);
void (*delete_inode) (struct inode *);
void (*put_super) (struct super_block *);
void (*write_super) (struct super_block *);
int (*sync_fs)(struct super_block *sb, int wait);
void (*write_super_lockfs) (struct super_block *);
void (*unlockfs) (struct super_block *);
int (*statfs) (struct dentry *, struct kstatfs *);
int (*remount_fs) (struct super_block *, int *, char *);
void (*clear_inode) (struct inode *);
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
};
所有的方法调用时除非特别要求,不会持有任何锁。这意味着,这些方法可以安全的阻塞。这些发放必须在进程的上下文中调用。(不能通过中断句柄或者下半部bottom half调用——没有进程上下文)
alloc_inode: 该方法由inode_alloc调用,为inode结构分配空间和初始化。如果未定义该方法,将分配一个简单的inode结构。通常,alloc_inode被用来分配一个包含inode结构的大型数据结构。
destroy_inode: 该方法由destroy_inode调用,用以释放为inode结构分配的资源。它只在alloc_inode方法被定义时有效,简单的逆向执行(undo)alloc_inode中的处理。
read_inode: 该方法被用来从挂载的文件系统中读取一个特定的inode。VFS设置inode结构中的i_ino成员来指示被读取得inode。其它的成员由该方法设置。
dirty_inode: VFS调用该方法来标记一个脏inode节点。
write_inode: VFS调用该方法将inode结构协会磁盘。第二个参数标示使用同步写还是异步方式,不是所有的文件系统都检查该标记。
put_inode: 在VFS的inode从cache中移除时调用。。
drop_inode: 在最后一个对该inode节点的访问操作被放弃时调用,该操作持有inode_lock自旋锁。该方法必须或为空(NULL,通常意义上的Unix文件系统语义),或为"generic_delete_inode"(为不需要缓存inode的文件系统,以使无论i_nlink为何值的情况下,都会调用"delete_inode")  "generic_delete_inode()"和曾经在 put_inode()中使用的"force_delete"行为相似,但是不会存在"force_delete"方法的竞争。
delete_inode: VFS调用该方法删除一个inode节点。
put_super; VFS调用该方法释放一个superblock(如umount)。在持有superblock锁时调用
write_super: 载VFS superblock需要写入磁盘时调用,该方法为可选。
sync_fs: 在VFS写一个superblock相关的所有inode节点时调用。第二个参数指示是否等待所有的写操作完成后再执行。可选。
write_super_lockfs: 在VFS锁住一个文件系统时调用,并强制进入一致状态。该方法现在由逻辑卷管理器(LVM)使用。
unlockfs; VFS调用该方法释放文件系统的锁,使其重新可写。
statfs:  在VFS需要获得文件系统统计信息时调用。该方法调用需要获得内核锁。
remount_fs: 在文件系统重新挂载时调用。该方法调用需要获得内核锁。
clear_inode: 在VFS清除一个inode节点试调用。可选。
umount_begin; 在VFS卸载文件系统时调用。
//sync_inodes; VFS写superblock关联的脏数据时调用。 --2.6.20中取消
show_options: 在VFS显示/proc/<pid>/mounts的挂载参数时调用。
show_stats:
quota_read: VFS调用该方法读取文件系统的配额文件。
quota_write; VFS调用该方法写入文件系统的配额文件。
read_inode()方法负责填充i_op域,该域是一个指向inode_operations结构的指针,该结构描述了每个inodes的操作方法。

四、inode对象

inode是文件系统中的对象元素。

1、inode_operations结构

该结构描述了VFS如何操作文件系统中的inode。在2.6.20中定义如下:

struct inode_operations {
 int (*create) (struct inode *,struct dentry *,int, struct nameidata *);
 struct dentry * (*lookup) (struct inode *,struct dentry *, 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 *);
 int (*mknod) (struct inode *,struct dentry *,int,dev_t);
 int (*rename) (struct inode *, struct dentry *,
   struct inode *, struct dentry *);
 int (*readlink) (struct dentry *, char __user *,int);
 void * (*follow_link) (struct dentry *, struct nameidata *);
 void (*put_link) (struct dentry *, struct nameidata *, void *);
 void (*truncate) (struct inode *);
 int (*permission) (struct inode *, int, struct nameidata *);
 int (*setattr) (struct dentry *, struct iattr *);
 int (*getattr) (struct vfsmount *mnt, struct dentry *, struct kstat *);
 int (*setxattr) (struct dentry *, const char *,const void *,size_t,int);
 ssize_t (*getxattr) (struct dentry *, const char *, void *, size_t);
 ssize_t (*listxattr) (struct dentry *, char *, size_t);
 int (*removexattr) (struct dentry *, const char *);
 void (*truncate_range)(struct inode *, loff_t, loff_t);
};

所有方法除非特别要求,调用时不必持有锁。

create:  由open(2)和create(2)系统调用调用。只在需要支持常规文件时必须定义。获得的目录结构不能存在inode(如,消极entry.如果有,就不需要创建了)。也许还需要为该dentry和新创建的inode调用d_instantiate()。

lookup:  在VFS需要从父目录里查找一个inode时调用。查找的名字从dentry中获得。该方法将找到的inode通过调用 d_add()插入到目录结构中。结构中的i_count项增1。如果该名字的inode不存在,则在dentry中增加一个空的inode(该 dentryb被称为消极dentry)

link:  由link(2)系统调用调用,只在需要支持硬链接(hard link)时必须定义。和在create()方法中类似,需要调用d_instantiate()。

ulink:  由ulink(2)系统调用调用,只在需要支持删除inode时必须定义。

symlink: 由symlink(2)系统调用调用,只在需要支持软链接(symbolick link)时必须定义。和在create()方法中类似,需要调用d_instantiate()。

mkdir:  由mkdir(2)系统调用调用,只在需要支持软链接(symbolick link)时必须定义。和在create()方法中类似,需要调用d_instantiate()。

rmdir:  由rmdir(2)系统调用调用,只在需要支持删除子目录时必须定义。

mknod:  由mknode(2)系统调用用来为设备(字符或块),命名管道或者套接字创建inode时调用,只在需要支持创建这类inode时必须定义。和在create()方法中类似,需要调用d_instantiate()。

rename:  由rename(2)系统调用调用,将源inode和dentry(前两个)改为目标inode和dentry(后两个)的父目录和文件名。

readlink: 由readlink(2)系统调用调用,只在需要支持软链接(symbolick link)时必须定义。

follow_link: 由VFS调用,用来获得一个软链接所指向的inode,只在需要支持软链接(symbolick link)时必须定义。该方法返回一个可以传给put_link()方法的空指针。

put_link: 由VFS调用,用来释放由follow_link分配的资源。该方法的最后一个参数是由follow_up返回的一个空指针的值参。它在页缓存不稳定的文件系统(如,NFS)中应用。

truncate: 由VFS调用,用来改变一个文件的大小。必须在该方法调用前用需要得大小来设置inode的i_size值。该方法被truncate(2)以及相关的系统调用调用。

permission: VFS调用该方法检查在POSIX类的文件系统上的访问权限。

serattr: VFS调用该方法设置文件的属性。该方法被chmod(2)以及相关的系统调用调用。

getattr: 获得文件属性。由stat(2)以及相关系统调用调用。

setxattr: VFS调用该方法设置文件的扩展属性信息。扩展属性是inode相关的"name:value"对。该方法由setxattr(2)系统调用调用。

getxattr; VFS调用该方法获得给定文件扩展属性的值。该方法由getxattr(2)系统调用调用。

listxattr: VFS调用该方法列出给定文件的所有扩展属性信息。该方法由listxattr(2)系统调用调用。

removexattr: VFS调用该方法移除给定文件的所有扩展属性信息。该方法由removexattr(2)系统调用调用。

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理