文件系统对象及装载

来源:互联网 发布:海康威视网络球机接线 编辑:程序博客网 时间:2024/06/05 16:37

(注,本文中用到的图片来《自存储技术原理分析》一书)

每个文件系统并不是独立使用的,相反,系统有一个公共根目录和全局文件系统树,要访问一个文件系统中的文件,必须先将这个文件系统放在全局文件系统树的某个目录下,这个过程叫文件系统挂载(mount),所装载到的目录叫装载点。
文件通过路径来标识。在linux公共文件模型下,目录和符号链接也是文件,只是他们有不同的操作接口,或者有不同的操作实现,上层通过系统调用操作文件系统,linux提供open read write mount等标准系统调用接口。
MInix是linux最早的文件系统,minix文件系统的磁盘布局由6部分组成:


其中i节点位图用于描述磁盘块上每个i节点的使用情况,除第一个比特位外,每个比特位表示一个i节点的使用情况。而编号为1保留给跟目录对应的i节点(别忘了目录也是文件)。

文件系统对象
linux文件系统对象之间的关系概况为文件系统类型,超级块,inode,dentry,vfsmount之间的关系,文件系统类型规定了某种文件系统的行为,主要目的是为了构造这种类型文件系统的实例。
超级快用于存放磁盘设备上文件系统结构的信息,在文件系统被装载时候,其内容被读入内存,用于构造内存中的超级块。其中某些信息为各种类型的文件系统所共有,被提炼成VFS超级块结构。
inode 反映某个文件系统对象的一般元信息,dentry反映某个文件系统对象在文件系统树中的位置,与超极块相同,inode及dentry也有磁盘上,内存中及VFS三种形式,其中VFS inode,VFS superblock, VFS dentry为各个类型文件系统共有的,而磁盘上,内存中的inode和dentry为具体文件系统特有。
linux有一颗全局文件系统树,反映linux VFS对象之间的关系,文件系统要被用户空间使用必须先装载到这棵树上,每一次装载被称为一个装载实例,某些文件系统只在内核中使用,也需要这样一个装载实例,一个文件系统装载实例四个必备元素:vfsmount,超级块,根inode跟根dentry。
一个文件系统类型可能有多个超级块实例,而每个超级块实例可能有多个装载实例,例如,分区/dev/sda1 ,/dev/sda2都被格式化问minix文件系统类型,当/dev/sda1/ dev/sda2上的文件系统实例被挂载到/mnt/d1,/mnt/d2,则有两个超级块实例,分别对应一个装载实例,当/dev/sda1再次被挂载到/mnt/d3下,则依旧还是只有2个超级块,但这时候sda1的超级块就有两个装载实例。
file_system_type:每种文件系统对应一个file_system_type,文件系统类型需要调用register_filesystem向VFS核心进行注册,调用unregister_filesystem从VFS中注销。
super_block:VFS 超级块(所有文件系统共有)
super_block描述了VFS层使用的超级块对象,对于具体的文件系统,需要定义自己的超级块对象,由super_block中的s_fs_info指向。




s_fs-info为了效率从磁盘复制到内存,基于磁盘的文件系统在分配或者释放块时候需要访问并分配位图,VFS允许这些文件系统直接在内存中,即超级块的s_fs_info上进行操作,而无须访问磁盘。
inode:VFS索引节点
inode包含了文件系统各种对象(文件,目录,块设备文件,字符设备文件等)的元数据。基于磁盘的文件系统,inode存在于磁盘(bdev_inode)上,其形式取决于文件系统的类型,在打开该对象进行访问时候,其inode被读入内存(minix-inode_info),内存有一部分是各种文件系统共有的,成为VFS inode(inode结构),在打开对象时候,还根据文件系统类型和对象类型设置inode操作表和file操作表。




下面我们看看各个结构存在的地方:
此外,每个inode对象视其状态,总是出现在下面一个循环双链表。
有效的未使用inode链表,这些inode不为脏,并且i_count(使用计数)为0,链表表头保存在inode_unused变量,这个链表可以看做inode缓存。
在使用中的inode链表,这些链表不为脏,i_count域大于0,链表的表头保存在inode_in_use变量中。
脏inode链表,链表表头为超级块bdi_writeback域的s_dirty域
inode中有两个操作表指针,i-op及i_fop,i_op中有个函数
int(*mknod)(struct inode*, struct dentry*,int, dev_t)他只是对代表目录的inode有意义,在目录中创建一个块(字符)special文件,同常规文件一样,块(字符)special文件也有自己的inode,但其中只需保存块(字符)special的主设备号和次设备号。
在调用这个函数之前,已经为块(字符)设备分配了dentry,但还没有分配inode,所以该操作:1 为块设备文件新建一个具体的inode描述符。2 修改目录内容,添加一项和块设备文件对应。3 将块设备文件的VFS inode的i_rdev设置。4 将块设备文件的VFS inode和dentry关联起来
dentry:VFS目录项
反映的是文件系统对象在内核中所在文件系统树中的位置。
打开对象时候,磁盘上的目录项被读取用来构造VFS dentry和内存目录项。




vfsmount:文件系统装载
vfsmount反映一个已装载的文件系统实例。内核代码可以通过vfsmount访问这个文件系统实例。
有了装载关系,全局文件系统树上的一个位置就不能由dentry唯一确定了,尤其是对于一个文件系统被装载到不同的装载点上。现在文件系统的位置用 <vfsmount,dentry>表示,这就是linux内核中确定文件位置的路径
文件系统的装载


上面的superblock,inode,dentry都是VFS中通用的数据结构,他们分别有指针指向各自的个性化结构。只是上图没有画出来。
上面就是一个文件系统装载实例,他们存在于linux内存中, 其他可以被内核其他部分使用,但是,如果需要从用户空间使用,就需要挂载到根文件系统上。
假定有/dev/sda1,/dev/sda2,/dev/sda3 /dev/sda4 ,/dev/sda1为根设备,其上的文件系统为根文件系统(这四个设备文件结点都在跟文件系统的/dev/目录下)
现在将某个设备上的文件系统装载到全局文件系统的某个目录下:
mount -t ext3  /dev/sda2  /mnt/d
这样将在/dev/sda2上的文件系统装载到目录/mnt/d下,/dev/sda2为包含这种类型的文件系统的设备文件名(他在之前已经被格式化为这种文件系统),/mnt/d为目标装载点,装载后,用户就可以通过/mnt/d/..访问设备 /dev/sda2上的文件系统
在啮合角度,装载过程,文件系统类型的get_sb将被调用,生成一个新的文件系统装载对象vfsmount,并和该文件系统类型的一个超级块实例关联起来。

mount系统调用的处理流程
装载文件系统的系统调用入口函数是sys_mount(fs/namespace.c),五个参数
dev_namr:包含文件系统的设备文件路径名
dir_name:文件系统被挂载到的目标路径名
type:文件系统类型名,必须在文件系统中已注册
flag:装载标识
data:文件系统特定的数据
sys_mount—>SYSCALL_DEFINE5(将用户空间数据复制到内核空间后调用do_mount)—>do_mount
1) do_mount->kernel_path在do_mount中,检查路径名是否有效,然后调用kernel_path,并通接着调用过它获得挂载点路径(<.vfmount,dentry>)
2)do_mount->do_new_mount对于新建挂载,do_mount 调用do_new_mount,新建挂载的实质:(A)构造子文件系统的装载实例,也就是建立各个元素之间的关系,并最终返回指向vfsmount描述符的指针(调用do_kern_mount),(B)将子系统的装载实例“挂钩到”父文件系统的装载点(调用do_add_mount)。
(A)do_new_mount->vfs_kernel_mount():构建文件系统装载实例,即构建vfsmount,super_block,根 dentry,根inode 间的关系(这里应该是具体的一个文件系统的根,不是全局的根文件系统的根),因为vfsmount 是一个纯内存的元素,利用alloc单独分配,其他三个与文件系统类型有关,虚调用它的get_sb回调函数完成,这样就构建了uper_block,根 dentry,根inode,并和同时传入的vfsmount关联。这中间还调用相关函数填充了内存中的超级块,以及inode位图等。
(B)do_new_mount->do_add_mount():添加到全局文件系统树。
路径查找
用户空间表示文件使用路径名字符串。而内核中对文件的操作则需要super_block,inode,dentry,vfsmount.路径查找由’/”的多个部分组成,过程就是分量解析,在推进的过程中,一步步在内核中构建关系。内核中表示路径的是path