插播一条广告,本人的《文件系统技术内幕》已经由电子工业出版社出版,该书非常系统的介绍了文件系统的相关内容,需要的同学可以直接下单购买。
前面我们对Linux内核的NFS实现进行了介绍。接下来我们介绍一个用户态的NFS服务端实现---NFS-Ganesha。NFS-Ganesha是一个用户态的NFS服务端,提供了与操作系统内核NFSD相同的功能。NFS-Ganesha的特点包括:在用户态实现、多协议支持和后端多存储类型支持。由于在用户态实现,因此很多其他特性的实现也比较方便。同时也是由于在用户态实现,对于数据的访问会出现内核态与用户态拷贝的情况,因此性能相对NFSD要差一些。
1Ganesha整体架构
NFS-Ganesha的整体架构与内核NFSD的架构类似,也是通过守护进程和函数指针的方式实现请求的分发处理。NFS-Ganesha没有实现自己的RPC库,而是使用了TI-RPC,关于该库的更多细节请参考[1],本书不再赘述。

NFS-Ganesha数据路径整体架构示意图
以NFS为例,其实现的函数如下所示。在下图中是所有实现的局部截图。当服务端收到数据包时,根据解析的结果会调用具体的处理函数进行处理。

具体命令的解析是在函数nfs_rpc_valid_NFS中完成的。对于NFSv3来说其相关代码逻辑如下所示:

2Ganesha的初始化
由于基于第三方RPC库,因此Ganesha只需要将关键的函数指针注册到该RPC库即可。这样当客户端有请求时,RPC库会自动找到对应的函数指针进行处理,这部分逻辑将会比较简单。

我们重点看一下Ganesha初始化的过程,具体代码如代码所示。通过阅读代码可以知道,该函数的主要功能是RPC模块的初始化(1262行)和函数指针的注册(1324行以下)。其中函数指针的注册又包含XPRT的创建(1324)和具体注册两部分逻辑。
其中XPRT创建由函数Create_SVCXPRTs完成,其内部完成了对不同协议XPRT的创建和初始化。这里主要是是对全局变量tcp_xprt的初始化的过程,而初始化中最重要的是将不同协议分发函数(例如nfs_rpc_dispatch_tcp_NFS)赋值给XPRT。
函数指针的注册是通过函数Register_program完成的,该函数最终调用了TI-RPC中的svc_reg函数来

大家如果能够理解上述注册的过程,就能够知道处理数据的函数是哪些,那么理解整个业务逻辑也就不会太复杂了。限于篇幅,本书不过多介绍,大家可以自行阅读Ganesha的代码。
Ganesha除了在用户态实现这个特点外,另外一个特点是实现对各种后端存储的支持,包括本地文件系统、Ceph、GlusterFS和GPFS等等很多文件系统。而对多种文件系统的支持是通过一个称为文件系统抽象层(FSAL)的模块来实现的。
本节我们重点介绍一下Ganesha的FSAL模块。该模块非常类似于Linux内核中的虚拟文件系统(VFS),它为协议处理函数提供了一个抽象层接口。所有协议的处理都调用该层面的接口, 然后FSAL的接口根据配置信息来调用具体后端的接口。这里具体后端是为Ceph和GlusterFS等实现的操作接口,我们可以将其与VFS中的具体文件系统相对应。如图5‑16是Ganesha源代码中FSAL与不同后端实现的目录结构。

FSAL及不同后端的目录结构
由于Ganesha中接口繁多,而逻辑又极其相似,因此我们这里就不费过多笔墨来介绍每个接口的逻辑细节了。本节我们以后端为Ceph为例介绍一个协议处理函数、FSAL函数、后端函数和Ceph文件系统API函数之间的对应关系。
在表5‑5中我们列出了最主要的几个函数,大家可以将该表对照源代码阅读,这样大家可以非常快速的理清Ganesha各个模块的关系。由于其它函数大同小异,因此本书并没有罗列全部函数。




