虚拟文件系统
VFS的层次
文件系统实现与用户进程(或C库)之间。
文件系统分类
基于磁盘的文件系统(ext2/3 fat iso9660…)、虚拟文件系统(proc)、网络文件系统(nfs)
通用文件模型
VFS提供一种结构模型,包含了一个强大的文件系统所应具备的所有组件。所有的文件系统实现,都必须提供与VFS定义的结构配合的例程,以弥补两种试图之间的差异。
文件描述符
一个整数,在用户层所有有关文件的操作中用于标识一个文件,在打开文件时由内核创建,特定于进程。
inode
l inode是什么?
² inode用来存放文件的元数据(例如,文件的创建者、文件的创建日期、文件的大小等等)
² 注意inode并不包含文件名。
l inode的内容
inode包含文件的元信息,具体来说有以下内容:
² 文件的size、文件属主的User ID、Group ID
² 文件的读、写、执行权限
² 文件的时间戳,:ctime(inode上一次变动的时间)、mtime(文件内容上一次变动的时间)、atime(文件上一次打开的时间)。
² 链接数,即有多少文件名指向这个inode。
² 文件数据分布在哪些磁盘Block。
l inode的状态
每个inode处于三种状态中的一个:
² inode位于内存中,未关联到文件;(inode_unused)
² inode位于内存中,由一个或多个进程使用,已与磁盘同步;(inode_in_used)
² inode处于活动使用状态,与磁盘上内容未同步,脏inode;
l inode是如何组织的?
² 内核使用两种方式组织inode。
² 链表:每个inode都有一个i_list成员,可将inode存储在链表中。(inode出现在特定于超级块的链表中i_sb_list,同时出现在特定于状态的链表中,例如inode_in_used)
² 散列:每个inode同时出现在一个散列表中(根据inode号快速访问inode)。
l 目录是什么?
² Linux下,目录也是文件,也有inode和数据部分,其数据部分内容如下:
第一部分表示对应inode的编号(系统内唯一),第二部分表示文件或目录的名字。
内核如何访问/user/bin/emacs
首先读取根目录(这个在内核中一直被维护,),在根目录文件的数据部分查找user这个目录项,根据其中的inode编号,获取inode。bin的查找类似,一直到查找到emacs,找到emacs对应的inode,该inode的数据部分即emacs这个文件(普通数据文件)的内容。
图示:
目录项缓存dentry:
l 引入dentry
上述访问/user/bin/emacs过程非常耗时,需要不断地读取inode和对应的数据部分。为了加速,内核将之前访问过的目录或文件(统称为目录项)缓存起来,下次再访问同样的目录项(例如,/user/bin/vi的/user/bin/部分)时,可以直接找到对应的inode(上图中为10号)。
l dentry的目的:
dentry的主要用途是建立文件名和inode之间的关联。
所以该结构体包括两个最主要的字段,d_inode和d_name。
其中,d_name为文件名。qstr是内核对字符串的封装(可以理解为带有散列值的char*)。
d_inode是与该文件名对应的inode。
l dentry结构体:
struct dentry {
…
/* Where the name belongs to - NULL is negative */
struct inode *d_inode;
struct qstr d_name;
struct dentry *d_parent; /* parent directory */
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 dentry_operations *d_op;
struct super_block *d_sb; /* The root of the dentry tree */
unsignedchard_iname[DNAME_INLINE_LEN_MIN]; /* small names */
…
};
l 什么时候创建
在VFS(以及文件系统实现)读取一个目录项(目录或普通文件)后,则创建一个dentry来缓存找到的数据。
l 内存中这些dentry如何组织管理
² 结构:各个dentry实例组成了一个网络,例如,当前dentry实例对应的所有文件和子目录相关联的dentry都归入到d_subdirs中。
² 组织方式一:内核所有活动的dentry实例都保存在一个散列表中,该散列表使用dentry_hashtable实现(全局dentry散列表)。
² 组织方式二:LRU链表,长时间不使用的dentry会被删除。
链接link
l 两类链接:符号链接(软链接)和硬链接。
l 符号链接(软链接)文件使用自己的inode,该inode的数据部分包含了一个字符串,给出了链接目标的路径。
l 硬链接创建时,使用了已有的inode编号。硬链接建立以后,无法区分原来的文件和新建的硬链接文件。这种情况下,inode使用计数器来确保文件删除操作中,当没有其他文件使用该inode时,才能真正删除该inode。
示意图:
特定于进程的信息
struct task_struct {
...
/* 进程的文件系统相关的信息*/
struct fs_struct *fs;
/* 该进程打开的文件 */
struct files_struct *files;
...
}
struct files_struct {
...
struct fdtable fdtab;
struct file * fd_array[NR_OPEN_DEFAULT];
};
struct file {
...
struct path *f_path;
loff_t f_pos;
...
};
struct file_operations {
...
ssize_t (*write) (struct file *, constchar __user *, size_t, loff_t *);
int (*readdir) (struct file *, void *, filldir_t);
unsignedint (*poll) (struct file *, struct poll_table_struct *);
int (*ioctl) (struct inode *, struct file *, unsignedint, unsignedlong);
int (*mmap) (struct file *, struct vm_area_struct *);
int (*open) (struct inode *, struct file *);
...
};
struct file_operations->open函数实质上是将一个file对象关联到一个inode。
poll函数也在其中。
从task到dentry/inode