[{"createTime":1735734952000,"id":1,"img":"hwy_ms_500_252.jpeg","link":"https://activity.huaweicloud.com/cps.html?fromacct=261f35b6-af54-4511-a2ca-910fa15905d1&utm_source=V1g3MDY4NTY=&utm_medium=cps&utm_campaign=201905","name":"华为云秒杀","status":9,"txt":"华为云38元秒杀","type":1,"updateTime":1735747411000,"userId":3},{"createTime":1736173885000,"id":2,"img":"txy_480_300.png","link":"https://cloud.tencent.com/act/cps/redirect?redirect=1077&cps_key=edb15096bfff75effaaa8c8bb66138bd&from=console","name":"腾讯云秒杀","status":9,"txt":"腾讯云限量秒杀","type":1,"updateTime":1736173885000,"userId":3},{"createTime":1736177492000,"id":3,"img":"aly_251_140.png","link":"https://www.aliyun.com/minisite/goods?userCode=pwp8kmv3","memo":"","name":"阿里云","status":9,"txt":"阿里云2折起","type":1,"updateTime":1736177492000,"userId":3},{"createTime":1735660800000,"id":4,"img":"vultr_560_300.png","link":"https://www.vultr.com/?ref=9603742-8H","name":"Vultr","status":9,"txt":"Vultr送$100","type":1,"updateTime":1735660800000,"userId":3},{"createTime":1735660800000,"id":5,"img":"jdy_663_320.jpg","link":"https://3.cn/2ay1-e5t","name":"京东云","status":9,"txt":"京东云特惠专区","type":1,"updateTime":1735660800000,"userId":3},{"createTime":1735660800000,"id":6,"img":"new_ads.png","link":"https://www.iodraw.com/ads","name":"发布广告","status":9,"txt":"发布广告","type":1,"updateTime":1735660800000,"userId":3},{"createTime":1735660800000,"id":7,"img":"yun_910_50.png","link":"https://activity.huaweicloud.com/discount_area_v5/index.html?fromacct=261f35b6-af54-4511-a2ca-910fa15905d1&utm_source=aXhpYW95YW5nOA===&utm_medium=cps&utm_campaign=201905","name":"底部","status":9,"txt":"高性能云服务器2折起","type":2,"updateTime":1735660800000,"userId":3}]
整体架构及流程
fuse为多线程并发模型,每个worker线程都在读取/dev/fuse中的请求,这样就保证线程之间的同步,当读取到一个请求之后,线程就开始处理该请求,但如果监听线程为0,则继续创建新的线程进行监听,处理完请求之后,如果线程的数量超过限制(10个),就退出该线程。整个框架如下:
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文件系统时,读取线程就会读取一个中断操作的请求,在下发该请求之前,先判断该请求是否为中断,如果是中断,则标记该中断,并将该请求从中断列表中删除该中断,在执行中断函数时,就调用对应请求的中断回调函数,其实也就是判断该请求是否完成,如果未完成则直接杀死请求对应的线程。如果线程已经完成,则在请求发送之前再次判断
该请求是否被中断,同样是处理对应的中断函数,如果该请求没有到达,则将该请求放入中断链表中,以便于在下次派发请求时,进行处理。整个请求的处理流程如下:
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。