用户空间文件系统(FUSE)( 三 )

每个通道都包含以下四个文件:

  • abort:写入任何数据到该文件都会导致所有请求被中断 , 阻塞的线程被唤醒;如果用户空间进程出现死锁或则无法响应时可以用此方法来终止内核模块的请求;
  • congestion_threshold:fuse通道中后台请求达到该数量后会进行限流操作;
  • max_background:fuse通道中最大的后台请求数量;
  • waiting:统计等待队列和处理队列中请求的数量;
3.6 FUSE请求如下是一个fuse请求的主要结构:
用户空间文件系统(FUSE)文章插图
主要包括了两个部分 , 发送给用户空间进程的部分fuse_in , 用户空间进程返回的部分fuse_out;每个部分都携带了请求头部和请求的参数;
struct fuse_in_header {uint32_tlen;# 头部和参数的总长度uint32_topcode;# 请求类型uint64_tunique;# 请求的唯一标识uint64_tnodeid;# 请求的inode iduint32_tuid;# 进行系统调用的用户iduint32_tgid;# 进行系统调用的用户组iduint32_tpid;# 进行系统调用的进程号uint32_tpadding;# 填充字段};struct fuse_out_header {uint32_tlen;# 头部和参数的总长度int32_terror;# 用户空间进程处理过程是否发送错误uint64_tunique;# 请求的唯一标识};fuse请求从创建到处理完成的过程中 , 会存在不同的状态:
用户空间文件系统(FUSE)文章插图
  • FUSE_REQ_INIT:请求创建后正在进行初始化;
  • FUSE_REQ_PENDING:请求初始化完成并加入了pending queue中 , 等待用户空间进程读取;
  • FUSE_REQ_READING:用户空间进程正在读取请求数据 , 加入了io queue中;
  • FUSE_REQ_SENT:请求数据读取完成 , 正在被用户空间进程处理 , 加入了processing queue中;
  • FUSE_REQ_WRITING:用户空间正在写入请求返回;
  • FUSE_REQ_FINISHEND:请求返回接收处理完成 , 将会唤醒睡眠线程;
3.7 初始化请求【用户空间文件系统(FUSE)】文件系统在挂载时 , 会向用户空间进程发送初始化请求 , 用于初始化用户空间文件系统 , 以及获取用户空间文件系统的特性 , 用于配置fuse通道属性 , 初始化请求正常接收后 , fuse通道才算初始化完成 , 其他请求才会发送到用户空间进程 , 文件系统可以配置如下的特性:
  • max_readahead:设置文件的最大预读 , 最大值为文件系统挂载时bdi设置的预读值 , 默认为VM_MAX_READAHEAD(32)页 , 可以在sysfs相应的bdi设备中修改;
  • flock:是否支持文件锁;
  • atomic_o_trunc:是否支持open时带O_TRUNC参数 , 支持则一同发送到用户空间进程 , 不支持则在open请求中去掉O_TRUNC标志位 , open请求完成后再发送设置文件大小属性为0的请求;
  • export_support:是否支持NFS导出;
  • big_writes:支持缓存io的多页写入 , 否则只会写入一页;
  • dont_mask:文件权限不适用掩码;
  • auto_inval_data:是否每次预读都去检查文件获取文件的大小属性;
  • do_readdirplus:是否支持readirplus回调;
  • async_dio:读取数据时 , 如果offset+count大于文件的大小 , 是否设置按FUSE_MAX_PAGES_PER_REQ(32)页对齐进行数据的读取;
  • max_write:一个写io的最大值 , 如果不设置 , 则默认值为4k;
  • max_background:设置后台请求的最大数量;
  • congestion_threshold:设置后台请求限流的阈值;
  • async_read:是否设置将预读请求作为后台请求;
3.8 后台请求除了来自用户进程直接的文件系统调用外 , 有部分请求会由内核的机制在后台进行创建 , 每个fuse通道中后台请求会有最大的数量 , 由max_background参数进行控制 , 主要包括了以下几类:
  • init:初始化请求在文件系统挂载时发送到用户空间进程 , 仅会发送一次;
  • release:当文件关闭后如果发现没有其他应用时 , 会发送release请求;
  • async_read:后台的异步读请求;
3.9 /dev/fuse之前在介绍中提到FUSE内核模块会将系统调用封装为fuse请求发送给用户空间进程 , 这个发送和接收的过程并不是直接进行的 , 而是由`/dev/fuse`这个杂项设备文件作为接口来提供了用户空间与内核空间的通信机制;对该文件的读会调用到`fuse_dev_read`方法 , 它会从fuse通道的队列中读取请求 , 没有请求则会阻塞 , 直到有请求来后被唤醒 。 对该文件的写会调用`fuse_dev_write` , 它会从用户空间进程读取请求返回的数据 , 并根据id在processing queue中找到对应的请求 , 填入写回数据并唤醒睡眠进程;
4
用户空间库
这里提到的用户空间库主要是libfuse , 它提供了开发用户空间文件系统便捷的两类C/C++接口 , 一类称为low_level , 能够用于直接操作文件系统的索引节点 , 一般文件系统都使用这类接口实现 , 它能够对文件进行灵活的控制;一类称为high_level , 其操作的是文件的绝对路径名称 , 底层也是由low_level接口进行实现 , 封装了fuse_path_ops接口回调用于处理索引节点到文件路径名称的转换 , 使用简单 , 但缺乏对文件灵活性;以下分析主要使用low_level进行分析 。