mount挂载时 no such device_新一代mount系统调用(2)——fsopen实现

029875dc8482210619e0973c5f3c0c5f.png

前言

上文(醉卧沙场:新一代mount系统调用(1)——接口初探)我们简要介绍了如何用新的一系列mount系统调用的接口来挂载一个文件系统。接下来,我们较深入看一下,这些系统调用具体做了什么。

现在,用新的系统调用创造一个mount挂载实例(完成一次普通挂载)的过程被分成了如下六个独立的步骤:

  1. 创建一个文件系统上下文实例。
  2. 解析挂载的参数,把他们附在文件系统上下文实例里。这些参数是通过用户层分开传送进来的。
  3. 预处理这个文件系统上下文实例,验证各参数间的合理关系(这步原来单独成步,后被合并到此步中)。创建(得到)一个超级块实例,以及一个root dentry。
  4. 实际执行挂载操作,创建挂载实例。
  5. 把返回的可能的错误信息附在文件系统上下文实例里。
  6. 销毁文件系统上下文实例。

根据我们上文提到的挂载一个文件系统的过程,简写如下(详细内容请翻看前一篇文章):

int 

我们第一个要说的就是fsopen这个系统调用。

fsopen(上)

首先是fsopen,我下面以mainline upstream linux 5.5-rc4为依据展开(不一样的内核可能会有不一样的行为):

/*                                                                                                                                                                                             

fsopen实现通过get_fs_type()从文件系统名称,也就是fsopen的第一个参数(如"xfs"),得到一个file_system_type实例。关于fs_system_type结构可以参考以前讲解旧mount系统调用时的这篇文章:醉卧沙场:文件系统怎么让Linux内核认识自己 。当前的file_system_type结构有所变化,主要是增加了一个新的方法:

struct 

这个方法从字面意思上就能看出来是初始化一个文件系统上下文实例,记住这个方法,我们下面马上说到。接着fsopen的下一步:

fc 

在通过文件系统名称得到一个文件系统类型实例后,下一步就是通过文件系统类型实例里的init_fs_context方法创建一个文件系统上下文实例。既然这个方法是在文件系统类型实例里,说明每个文件系统都可能有对其不同的实现。fs_context_for_mount的实际调用过程就是:

fs_context_for_mount --> alloc_fs_context --> (fs_type->init_fs_context)

第一个函数只是一层封装,直接调用的alloc_fs_context。最主要的作用就是传递一个参数:

struct 

FS_CONTEXT_FOR_MOUNT这个参数指明我们需要创建一个FOR_MOUNT的上下文。

文件系统行下文 fs_context

文件系统上下文这个概念是这次new mount API新引入的,在继续讲解之前我们有必要先来看一下其定义:

struct 

我们捡主要的域说一下:

  • const struct fs_context_operations *ops;

这是一个文件系统上下文实例持续期间,提供给文件系统上下文使用的众多方法。一般由特定文件系统类型的init_fs_context方法来对其进行设置。

  • struct file_system_type *fs_type;

这是一个用来指向即将被挂载(或重新配置)的文件系统所属文件系统类型实例的指针。换句话说,用例中我们要挂载一个XFS,这里就是指向XFS的file_system_type结构指针。

  • void *fs_private;

这是一个指向文件系统私有数据的指针,常用于存储需要特定文件系统来解析的选项。

  • struct dentry *root;

这是一个指向一个可挂载文件系统的根节点的指针,同时它也和文件系统的superblock关联。这个域一般通过vfs_get_tree调用特定文件系统的->get_tree函数来构建。同时,为了构建SB,也会调用特定文件系统的fill_super函数。

  • char *source

这个代表挂载操作的源对象,一般就是一个设备(上面含有文件系统)。如mount /dev/sda1 /mnt中,"/dev/sda1"就是这个source的名称。当然它还可以是非本地文件系统时使用的类似url:/path这样的写法。

  • void *s_fs_info

这是区别于内存通用superblock结构的一个域,用于指向特定文件系统的私有信息。

  • unsigned int sb_flags;

这其实是挂载flag在superblock里的一个镜像。比如SB_RDONLY, SB_NODEV, SB_NOATIME等对分别对应MS_*的flag。

  • enum fs_context_purpose purpose:8

指明当前的文件系统上下文的使用意图,目前有如下三种使用意图:

enum 

依次为为了一次mount,为了一次submount,为了一次remount。

  • enum fs_context_phase phase:8

指明当前处于文件系统挂载过程的哪个阶段,虽然我们在本文最开始说了现在将挂载过程分成独立的六个步骤,但下面这7个阶段的定义和上面说的并没有直接关系。

/*                                                                                                                                                                                             

alloc_fs_context --> init_fs_context

说完文件系统上下文的定义,我们可以从alloc_fs_context开始继续说了:

/**                                                                                                                                                                                            

这个函数说的很明确,就是用来创建一个文件系统上下文实例。

  1. 先分配空间,创建一个文件系统上下文结构体。
  2. 然后初始化其中几个域,主要是purpose, fs_type, sb_flags这几个(当然其它的也很重要,只是我们不需要在这里说) 对于FOR_MOUNT的purpose来说,sb_flags这里先是0。
  3. 接着对于不同的purpose,使用get_user_ns来初始化不同的fc->user_ns。关于namespace这里我们也先不多说,对我们后面的讲述暂时不构成干扰。
  4. 接着就是fc->fs_type->init_fs_context登场了,这个我们前面就提到了。一般的文件系统都具有自己的init_fs_context函数,如果没有,则使用legacy_init_fs_context这个函数代替。

下面就是说init_fs_context的时候了,由于每个文件系统都可能有自己的实现。我们以XFS为例进行简要的说明(因为要设计XFS的结构,我们没有讲过):

static 

对于不同的文件系统来说,这里应该有不一样的处理。但是最主要的就是初始化文件系统上下文的ops, s_fs_info, sb_flags等域中某一个或几个。

对于XFS来说,就是构建了一个XFS的挂载实例xfs_mount,经过必要的初始化后把它放到s_fs_info上。我们上面说过了s_fs_info是指向文件系统特有的私有数据,这里也可以看出xfs_mount就是专门给XFS用的,因为我们这里不讲XFS,所以先不展开xfs_mount。

然后还用xfs_context_ops设置了fc->ops。我们可以看一下一般会为文件系统上下文提供什么方法:

static const struct fs_context_operations xfs_context_ops = {
        .parse_param = xfs_fc_parse_param,
        .get_tree    = xfs_fc_get_tree,
        .reconfigure = xfs_fc_reconfigure,
        .free        = xfs_fc_free,
};

从函数名我们就能推测出这几个方法是干什么用的。其中第一个很重要,就是用来解析文件系统特有的挂载选择的。第二个我们上面说过了,是用来得到root dentry的。第三个和第四个也顾名思义。

经过init_fs_context的初始化,alloc_fs_context返回一个初步创建完成的文件系统上下文"return fc;"。我们返回fsopen继续看。

fsopen(下)

一个文件系统上下文已经分配空间完成,设置fc->phase为FS_CONTEXT_CREATE_PARAMS阶段

fc

一个文件系统上下文处于哪个阶段可以在后续使用,比如通过phase来知道这个上下文正处于什么阶段,以便判断当前要做的操作是否可以在这个阶段进行。

接着:

ret = fscontext_alloc_log(fc);

用来创建一块内存空间,放在文件系统上下文中,用来记录文件系统上下文使用使其的各种日志信息。一般通过logfc()函数或其封装函数来记录日志,通过fscontext_read或其封装来读取。

最后,因为我们知道fsopen是要返回一个描述符(fd)的,所以这最后一步就是:

return 

将创建出来的文件系统上下文实例和一个文件描述符关联,并返回文件描述符:

/*                                                                                                                                                                                             

这也符合Linux一切皆文件的部分思想。这里用anon_inode_getfd的实现如下:

int 

首先

error = get_unused_fd_flags(flags);

得到一可用的fd文件描述符。

然后

file = anon_inode_getfile(name, fops, priv, flags);

创建一个"[fscontext]"类型的文件实例,把他和一个匿名的inode关联,并创建一个dentry来关联这个文件。这里有一个需要注意的地方就是再上面 &fscontext_fops 这个参数被赋值为当前创建的文件实例的默认文件方法集(file_operations),这不同于我们以往对普通的文件对象的file_operations的认识,fscontext_fops的定义是:

const 

其中fscontext_read我上面提到过,后面的我们这里暂时不讲(看名字的意思也能明白一些)。

这里还有一个需要注意的地方就是,我们把文件系统上下文实例fc,通过priv参数传给了anon_inode_getfile()函数,然后anon_inode_getfile()执行了:

file->private_data = priv;

也就是说将文件系统上下文作为文件的private_data私有数据,和文件实例关联了起来。这点需要记住,因为这里最主要做的就是文件系统实例和文件实例的关联。后面我们还要从文件实例里拿出文件系统实例,我们得知道是从private_data这里拿。

最后

fd_install(fd, file);

将文件描述符fd,和文件实例file关联。返回fd。

结语

到此就完成了fsopen的全过程,创建一个文件系统上下文,初始化一部分区域,将上下文和一个文件实例关联,并用一个文件描述符做索引,返回文件描述符。到此我们可以认为完成了文章开始时说的六个步骤中的第一个步骤。后面的文章我们将再继续看后面的步骤。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值