FUSE架构详解

整体架构及流程

fuse为多线程并发模型,每个worker线程都在读取/dev/fuse中的请求,这样就保证线程之间的同步,当读取到一个请求之后,线程就开始处理该请求,但如果监听线程为0,则继续创建新的线程进行监听,处理完请求之后,如果线程的数量超过限制(10个),就退出该线程。整个框架如下:

FUSE架构详解_第1张图片

 

worker线程通过请求派发函数fuse_ll_process派发请求之后,根据opcode转发到具体的操作(如init操作,则为do_init{lib/fuse_lowlevel.c}),它通过判断头部之后,提取出具体的参数,如果头部不合法,则直接向/dev/fuse发送错误信息,否则将请求路由到fuse_lib_opcode(如init,则fuse_lib_init{lib/fuse.c}),执行与fuse相关的操作,以及根据操作查找对应的缓存,将参数转换为用户可以识别的参数,接着就调用用户注册函数,等待用户返回,将结果立即回写至/dev/fuse设备中。回写之后,该线程就变为空闲状态,判断线程数量是否超过限制,如果超过限制则该线程退出,否则继续读取下一个请求。

具体流程如下:

FUSE架构详解_第2张图片

中断处理

当上层应用程序中断访问fuse文件系统时,读取线程就会读取一个中断操作的请求,在下发该请求之前,先判断该请求是否为中断,如果是中断,则标记该中断,并将该请求从中断列表中删除该中断,在执行中断函数时,就调用对应请求的中断回调函数,其实也就是判断该请求是否完成,如果未完成则直接杀死请求对应的线程。如果线程已经完成,则在请求发送之前再次判断 该请求是否被中断,同样是处理对应的中断函数,如果该请求没有到达,则将该请求放入中断链表中,以便于在下次派发请求时,进行处理。整个请求的处理流程如下:

  FUSE架构详解_第3张图片

fuse_prepare_interrupt()函数主要是判断该请求是否被中断,如果处于中断状态,则调用系统的中断处理函数,即直接杀死该线程,由内核中断请求的数据包来进行释放该请求的所有信息。fuse_finish_interrupt()函数操作类似于fuse_prepare_interrupt,区别是一个在请求之前,未完成阶段(finished=0),而另一个则是请求之后,已完成请求的处理(finished=1)。

读写设备

        将文件系统挂载至/dev/fuse上时,需要指定一次读取请求的缓冲区大小,其中最大为128K,而fuse客户端中指定为132K,读函数为read。

        当向设备中写回复时,则没有指明写缓冲区的大小,写函数为writev,一般为2项:头部,相关的回复。如果出现错误则只有头部。

缓冲区管理

为了减少内核中大量的lookup请求,fuse客户端采用hash算法实现了一个缓冲区,当出现lookup操作时,通过(ino,name)来对父索引节点号进行hash,再便利具体的子节点,最后查找到name,进行返回,如果查找失败,则该缓冲区向用户注册的函数发送getattr请求,最后将获取到的数据进行缓存。

其中节点的存放采用了树形结构,每个节点包含父节点以及子节点,而当查找到一个文件夹中的文件时,就采用逆向遍历来获取文件的绝对路径。

       缓存区主要有两种类型的缓冲区:name_hash,id_hash,第一种主要利用(ino,name)来进行hash,而第二种只针对ino进行hash。

   

 

 

 

 

 

你可能感兴趣的:(分布式存储)