对linuxVFS的理解

讨论linuxVFS是个很沉重的话题, 个人觉得,从源码上分析确实不太明智,第一,看完分析完就忘,第二,太浪费时间,懂了后也无法应用在实际场合中,所以,理清脉络个人觉得对内核的学习是最重要的,理清实现的思路,之后在以后真的要应用时详细的分析代码细节,所以本文讨论VFS主要围绕实现机制,围绕以下几点来说明
1.什么是VFS
2.inode, dentry
3.文件系统的注册,挂载
4.如何实现不同文件系统之间的COPY
下面还是逐条分析
■什么是VFS
VFS是软件, 一个什么样的软件呢? 是一个用来管理多个实际文件系统的软件
比如linux系统下有两个实际的文件系统, 一个fat32类型磁盘A , 一个ext3类型磁盘B,  如果我要将A上的一个文件1.txt拷贝到B中去,我只需要在中端敲击命令 cp  path1/1.txt   path2
就可以了, 而底层会如何做的?
1.根据path1找到对应的1.txt文件标识(inode 1)
2.调用inode 1对应的拷贝函数(此拷贝函数对应的是fat32类型的),将磁盘中的1.txt内容读入高速缓存中
3.根据path2找到对应目的地的inode 2
4.调用inode 2对应的拷贝函数(此拷贝函数对应的是ext3类型的),将高速缓存中1.txt内容拷贝到path2指定的目的地
这1-4的过程就是VFS其中的一部分
■inode, dentry
关于inode,dentry,网上的解释也是一堆一堆的,说下我自己的理解
在实际文件系统中,即磁盘中,有inode存储区域, 这个存储区域中每一个inode代表着一个文件,这里具体以minix3文件系统来举例


每个文件都有一个inode与dentry,而dentry是用来搜索文件用
dentry在VFS中,它的存在是为了路径的搜索, 比如/mnt/1.txt
/      mnt/       1.txt
对应的map是
dentry=/  dentry=mnt/  dentry=1.txt
路径结构关系: dentry("/")<--(parent)--dentry("mnt/")<--(parent)--dentry("1.txt")
也就是说,open文件时,根据dentry关系去寻找,最后找到1.txt对应的dentry
对于1.txt来说
dentry->d_inode == 1.txt的inode
下图给出 inode与dentry的关系


■文件系统的注册,挂载
以linux3.2.0, ext4文件系统为例,当执行挂载命令时
调用关系如下
SYSCALL_DEFINE5
     |
     |----do_mount
             |
             |----do_new_mount
                       |----do_kern_mount
                       |         |
                       |         |----vfs_kern_mount
                       |                      |
                       |                      |----mount_fs
                       |                               |
                       |                               |----type->mount(ext4_mount)
                       |----do_add_mount                               


 接下来分析一下这些函数都是做什么用的
1.do_mount
 当执行mount -t ext4..挂载命令时, 会执行此函数
 因为mount命令有很多的形式,比如remount的等等,因此,do_mount会对这些情况作区分,当然如果紧紧是挂载,那么会执行do_new_mount的分支
2.do_new_mount
 定义一个 vfsmount结构的变量mnt,
 调用vfs_kern_mount返回挂载后的mnt: mnt = do_kern_mount
 do_add_mount将mnt链到内核维护的vfsmount结构中,这个地方多说一嘴,可能不太对,因为源码看的比较糙
 do_add_mount(mnt, path, mnt_flags);
 这个函数参数mnt为vfsmount结构, 其中mnt与挂载的根dentry: root已经建立的联系,path是挂载的路径名称
 这个函数是将挂载的路径名称与mnt建立了联系
 举个例子:假设 /home/abc/1.txt 这里abc目录是一个挂载点,在打开1.txt的过程中获取到了abc目录的inode,通过inode发现是个挂载点
 那么内核会在维护vfsmount的hash里去找,会根据路径"/home/abc"去找对应的vfsmnount结构mnt,找到后,获取vfsmount中的dentry,即mnt->mnt_root
 即挂载点的entry,之后找到对应的inode,即dentry->d_inode,之后找到对应的1.txt文件,如果大家这个地方不太懂可以往下看,回头再来看这段描述
3.do_kern_mount
 首先通过struct file_system_type *type = get_fs_type(fstype);
 获取type类型,这个操作是根据什么来的呢?
 ①.在super.c文件中的最后
     module_init(ext4_init_fs)
     module_exit(ext4_exit_fs)
     所以在系统刚启动时,会调用ext4_init_fs
 ②.在ext4_init_fs中会调用函数register_filesystem(&ext4_fs_type);
     对ext4_fs_type进行注册,会将ext4_fs_type中的.name="ext4"注册到内核的全局变量file_systems
     中
 ③.ext4_fs_type
     ext4_fs_type是提前定义好的
     static struct file_system_type ext4_fs_type = {
.owner = THIS_MODULE,
.name = "ext4",
.mount = ext4_mount,
.kill_sb = kill_block_super,
.fs_flags = FS_REQUIRES_DEV,
     };
  所以,get_fs_type(fstype);函数中 fstype传入的是"ext4",函数会在file_systems
  中寻找"ext4"对应的fs_type即 ext4_fs_type,之后调用vfs_kern_mount
4.vfs_kern_mount
  ①.首先定义两个变量
   struct vfsmount *mnt; //完成挂载后的mnt
   struct dentry *root;  //完成挂载后的root dentry
  ②.malloc出一个mnt,根据名字来分配
    mnt = alloc_vfsmnt(name); 其中name是设备名,比如在/dev/sdaxx的mem_cache中分配一个地方
  ③.获取根dentry, root = mount_fs(type, flags, name, data);
  ④.建立 mnt与root的关系
  mnt->mnt_root = root;
mnt->mnt_sb = root->d_sb;
mnt->mnt_mountpoint = mnt->mnt_root;
mnt->mnt_parent = mnt; //在do_add_mount会对mnt_parent进行更改
5.mount_fs没什么好说的,调用mount_fs
6.mount_fs
  调用type->mount,其中type是do_kern_mount传下来的,即ext4_fs_type
  最后调用ext4_mount也就是我们要重点讨论的对象
对于挂载来说, 我们要研究一下ext4_mount这个函数,也就是VFS比较重要的地方都在这里了,当然我们也是本着理清脉络的方式去研究
下图给出调用关系
 ext4_mount
      |
      |----mount_bdev
              |
              |----ext4_fill_super
                        |
                        |----ext4_iget
                        |
                        |----d_alloc_root
                                 |
                                 |----__d_alloc
                                 |----d_instantiate
 对于ext4_mount来讲,我认为比较重要的地方如上
 这里说明一下ext4_fill_super函数
 1.ext4_fill_super
  首先获取了超级快结构sb
  接着调用root = ext4_iget(sb, EXT4_ROOT_INO);获取根"/"的inode
 2.ext4_iget
  从磁盘获取root的inode后(因为"/"是目录,即对inode的i_op进行赋值)
  inode->i_op = &ext4_dir_inode_operations;
  inode->i_fop = &ext4_dir_operations; 这里便是VFS重点所在
  举个例子: 当我们把ext4类型的磁盘设备挂载到根目录"/"下,由于挂载时将inode赋值成ext4类型
  在"/"目录下创建文件1.txt
  那么"/"所对饮inode,即调用inode->i_op->ext4_create去创建文件
  ext4_create函数会先创建1.txt对应的inode
  inode = ext4_new_inode(handle, dir, mode, &dentry->d_name, 0, NULL);
  然后对inode的i_op赋值成ext4类型
  inode->i_op = &ext4_file_inode_operations;
  inode->i_fop = &ext4_file_operations;
 3.d_alloc_root
  创建一个dentry名字为"/"
  将inode与entry建立联系

  dentry->d_inode = inode;

 ■如何实现不同文件系统之间的COPY

 给出一个关系图:

我们要把 /dnw/1.txt 拷贝到 /abc下
1.首先要明确/ 与/abc是两个挂载点
ext4磁盘挂载到了/下 ,所以挂载后/的inode操作对应的是ext4的操作
fat32磁盘挂载到了 /abc下 ,我们知道abc文件夹的生成时通过"/"的ext4_create创建的
挂载了fat32后, 在abc inode下的s_mount会被置1, 等目录搜索到/abc时会找到vfsmountB
从而得到dentry->i_node,即找到在/abc在挂载的"/"的inode,于是换成此"/"目录的inode操作函数,即fat32_create等去操作文件

 总结:通过以上脉络分析,我们总结如下
  1. 一个文件对应一个inode与entry结构,
  2. 因为文件与文件的不同对应的inode->i_op不同 ==>这就是VFS的精髓
  比如打开一个文件A,调用这个文件的inode-i_fop->open
  那么对应的这个open函数完全是根据A的类型来,如果A是ext4类型,则调用ext4_open
  如果是个字符设备,则调用字符设备open函数,即驱动中实现的open函数,关于字符设备的open函数机制也是VFS的一部分,在这里不作讨论


  • 2
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值