Linux虚拟文件系统(VFS)学习

虚拟文件系统(Virtual Filesystem)也可称之为虚拟文件系统转换(Virtual Filesystem Switch),是一个内核软件层,用来处理与Unix标准文件系统相关的全部系统调用。其健壮性表如今能为各种文件系统提供一个通用的接口。

通用文件系统模型

VFS所隐含的主要思想在于引入一个通用的文件系统模型(common file model),这个模型可以表示全部支持的文件系统。在通用文件模型中,每一个文件夹被看做一个文件,可以包括若干文件和其它的子文件夹。

通用文件模型由下列对象类型组成:

超级块对象(superblock object)

存放已安装文件系统的有关信息(A superblock object represents a mounted filesystem)。对基于磁盘的文件系统,这类对象通常相应于存放在磁盘上的文件系统控制块

索引节点对象(inode object)

存放关于详细文件的一般信息(An inode object represents an object within the filesystem)。对于基于磁盘的文件系统,这类对象通常相应于存放在磁盘上的文件控制块。每一个索引节点对象都有一个索引节点号,这个节点号唯一地标识文件系统中的文件。

文件对象(file object)

存放打开文件与进程之间进行交互的有关信息(A file object represents a file opened by a process)。这类信息仅当进程訪问文件期间存在于内核内存中。

文件夹项对象(dentry object)

存放文件夹项(也就是文件的特定名称)与相应文件进行链接的有关信息。

VFS的数据结构

这里仅仅列举和进程相关的结构

索引节点对象

文件系统处理文件所须要的全部信息都放在一个名为索引节点的数据结构中。文件名称能够随时更改,可是索引节点对文件是唯一的,而且随着文件的存在而存在。内存中索引节点对象由一个struct inode数据结构构成。

struct inode {
struct hlist_node i_hash; //用于散列链表
struct list_head i_list; /* backing dev IO 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; //上次改动索引节点的时间
blkcnt_t i_blocks; //文件的块数
unsigned int i_blkbits; //块的位数
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; //在直接I/O文件操作中避免出现竞争条件的读写信号量
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; //指向address_space对象的指针
struct address_space i_data; //文件的address_space对象
#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; //指向字符设备驱动程序的指针
}; __u32 i_generation; //索引节点的版本 #ifdef CONFIG_FSNOTIFY
__u32 i_fsnotify_mask; /* all events this inode cares about */
struct hlist_head i_fsnotify_mark_entries; /* fsnotify mark entries */
#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
#ifdef CONFIG_FS_POSIX_ACL
struct posix_acl *i_acl;
struct posix_acl *i_default_acl;
#endif
void *i_private; /* fs or device private pointer */
};

文件对象

文件对象描写叙述进程如何与一个打开的文件进行交互。文件对象是在文件被打开时创建的,由一个file结构组成。文件对象在磁盘上是没有相应的映像,因此file结构中没有设置“脏”字段来表示文件对象是否已被改动。

struct file {
/*
* fu_list becomes invalid after file_free is called and queued via
* fu_rcuhead for RCU freeing
*/
union {
struct list_head fu_list;
struct rcu_head fu_rcuhead;
} f_u;
struct path f_path;
#define f_dentry f_path.dentry //与文件相关的文件夹项对象
#define f_vfsmnt f_path.mnt //含有该文件的已安装文件系统
const struct file_operations *f_op; //文件操作表指针
spinlock_t f_lock; /* f_ep_links, f_flags, no IRQ */
atomic_long_t f_count; //文件对象的引用计数器
unsigned int f_flags; //当打开文件时所指定的标志
fmode_t f_mode; //进程訪问模式
loff_t f_pos; //当前的文件偏移量
struct fown_struct f_owner; //通过信号进行I/O事件通知的数据
const struct cred *f_cred;
struct file_ra_state f_ra; //文件预读状态 u64 f_version; //版本,每次使用后自己主动递增
#ifdef CONFIG_SECURITY
void *f_security;
#endif
/* needed for tty driver, and maybe others */
void *private_data;//指向特定文件系统或设备驱动程序所须要数据的指针 #ifdef CONFIG_EPOLL
/* Used by fs/eventpoll.c to link all the hooks to this file */
struct list_head f_ep_links;//文件的事件轮询等待着链表头
#endif /* #ifdef CONFIG_EPOLL */
struct address_space *f_mapping;//指向文件地址空间对象的指针
#ifdef CONFIG_DEBUG_WRITECOUNT
unsigned long f_mnt_write_state;
#endif
};

文件对象通过一个名为filp的slab快速缓存分配,filp描写叙述符地址存放在filp_cachep变量中。因为分配的文件对象数目是有限的,因此files_stat变量在其max_files字段中指定了可分配的文件对象的最大数目,也就是系统可同一时候訪问的最大文件数。

内核初始化期间,files_init()函数把max_files字段设置为可用RAM大小的1/10。只是,系统管理员能够通过写/proc/sys/fs/file-max文件来改动这个值。并且即使max_files个文件对象已经被分配,超级用户也总是能够获得一个文件对象

void __init files_init(unsigned long mempages)
{
int n; filp_cachep = kmem_cache_create("filp", sizeof(struct file), 0,
SLAB_HWCACHE_ALIGN | SLAB_PANIC, NULL); /*
* One file with associated inode and dcache is very roughly 1K.
* Per default don't use more than 10% of our memory for files.
*/ n = (mempages * (PAGE_SIZE / 1024)) / 10;
files_stat.max_files = n;
if (files_stat.max_files < NR_FILE)
files_stat.max_files = NR_FILE;
files_defer_init();
percpu_counter_init(&nr_files, 0);
}

文件夹项对象

VFS把每一个文件夹看做若干子文件夹和文件组成的一个普通文件。一旦文件夹项被读入内存,VFS就把它转换成基于dentry结构的一个文件夹项对象。对于进程查找的路径名中的每一个分量,内核都为其创建一个文件夹项对象;文件夹项对象将每一个分量与其相应的索引节点相联系。比如在查找路径名/tmp/test时,内核为根文件夹“/”创建一个文件夹项对象,为根文件夹下的tmp项创建第二级文件夹项对象,为/tmp文件夹下的test创建一个第三级文件夹项对象。

文件夹项对象在磁盘上并没有相应的映像,因此在dentry结构中不包括指出该对象已被改动的字段。文件夹项对象存放在名为dentry_cache的快速缓存中。

struct dentry {
atomic_t d_count; //文件夹项对象引用计数
unsigned int d_flags; /* 文件夹项快速缓存标志protected by d_lock */
spinlock_t d_lock; /* per dentry lock */
int d_mounted; //对文件夹而言,用于记录安装该文件夹项的文件系统计数器
struct inode *d_inode; /* 与文件名称关联的索引节点Where the name belongs to - NULL is
* negative */
/*
* The next three fields are touched by __d_lookup. Place them here
* so they all fit in a cache line.
*/
struct hlist_node d_hash; /* lookup hash list */
struct dentry *d_parent; /* 父文件夹的文件夹项对象parent directory */
struct qstr d_name; //文件名称 struct list_head d_lru; /* LRU list */
/*
* d_child and d_rcu can share memory
*/
union {
struct list_head d_child; /* child of parent list */
struct rcu_head d_rcu;
} d_u;
struct list_head d_subdirs; /* our children */
struct list_head d_alias; /* inode alias list */
unsigned long d_time; /* used by d_revalidate */
const struct dentry_operations *d_op;//文件夹项方法
struct super_block *d_sb; /* 文件的超级块对象The root of the dentry tree */
void *d_fsdata; /* 依赖于文件系统的数据fs-specific data */ unsigned char d_iname[DNAME_INLINE_LEN_MIN]; /* small names */
};

与进程相关的文件

每一个进程都有它自己当前的工作文件夹和他自己的根文件夹。这不过内核用来表示进程与文件系统相互作用所必须维护的数据的两个样例。类型为fs_struct的整个数据结构就用于此目的

struct fs_struct {
int users;
rwlock_t lock;
int umask; //当打开文件设置文件权限是所用的位掩码
int in_exec;
struct path root, pwd; /* 根文件夹的文件夹项,根文件夹所安装的文件系统对象
当前工作的文件夹项,当前工作文件夹所安装的文件系统对象*/
};

进程当前打开的文件与files_struct结构有关

struct fdtable {
unsigned int max_fds; //文件对象的当前最大数目
struct file ** fd; /* current fd array */
fd_set *close_on_exec;
fd_set *open_fds;
struct rcu_head rcu;
struct fdtable *next;
}; /*
* Open file table structure
*/
struct files_struct {
/*
* read mostly part
*/
atomic_t count; //共享进程的数目
struct fdtable *fdt; //
struct fdtable fdtab;
/*
* written part on a separate cache line in SMP
*/
spinlock_t file_lock ____cacheline_aligned_in_smp;
int next_fd;
struct embedded_fd_set close_on_exec_init;
struct embedded_fd_set open_fds_init;
struct file * fd_array[NR_OPEN_DEFAULT];//文件对象指针的初始化数组
};

fd字段指向文件对象的指针数组。该数组的长度存放在max_fds字段中。通常,fd字段指向files_struct结构中的fd_array字段,该字段包含32个文件对象指针。假设进程打开的文件数据多于32,内核就分配一个新的、更大的文件指针数组,并将其地址存放在fd字段中,内核同一时候更新max_fds字段的值。

相应fd数组中有元素的每一个文件来说,数组的索引就是文件描写叙述符。通常,数组的第一个元素(索引0)是进程的标准输入文件,数组的第二个元素(索引1)是进程的标准输出文件,数组的第三个元素(索引2)是进程的标准错误输出文件。

内核在进程描写叙述符的signal->rlim[RLIM_NLIMITS]结构上强制动态限制文件描写叙述符的最大数;这个值通常为1024,但假设进程具有超级权限,就能够增大这个值。

最经常使用的特殊文件系统类型

名字           安装点     说明

bdev           无        块设备

binfmt_misc    随意      其它可运行格式

devpts         /dev/pts  伪终端支持

eventpollfs    无        由有效事件轮询机制使用

futexfs        无        由futex(高速用户态加锁)机制使用

pipefs         无        管道

proc           /proc     对内核数据结构的常规訪问点

rootfs         无        为启动阶段提供一个空的根文件夹

shm            无        IPC共享线性区

mqueue         随意       实现POSIX消息队列时使用

sockfs         无         套接字

sysfs          /sys       对系统參数的常规訪问

tmpfs          随意        暂时文件(假设不被交换出去就保持在RAM中)

usbfs          /proc/bus/usb USB设备

文件系统注冊

每一个注冊的文件系统都用一个类型为file_system_type的对象来表示

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; struct lock_class_key i_lock_key;
struct lock_class_key i_mutex_key;
struct lock_class_key i_mutex_dir_key;
struct lock_class_key i_alloc_sem_key;
};

以sockfs文件系统注冊为例

static struct file_system_type sock_fs_type = {
.name = "sockfs",
.get_sb = sockfs_get_sb,
.kill_sb = kill_anon_super,
}; static int __init sock_init(void)
{
/*
* Initialize sock SLAB cache.
*/ sk_init(); /*
* Initialize skbuff SLAB cache
*/
skb_init(); /*
* Initialize the protocols module.
*/ init_inodecache();
/* 注冊sockfs文件系统 */
register_filesystem(&sock_fs_type);
sock_mnt = kern_mount(&sock_fs_type); /* The real protocol initialization is performed in later initcalls.
*/ #ifdef CONFIG_NETFILTER
netfilter_init();
#endif return 0;
}
上一篇:JAVA中java.util.Date、java.sql.Timestamp和String之间的互相转换


下一篇:linux 虚拟文件系统