精华内容
下载资源
问答
  • fusemount
    千次阅读
    2017-04-20 16:56:00

     

    在bbfs中,传递的参数有两个目录,fuse将一个目录挂载在另一个目录下。

    在ssfs中,传递的参数只有一个目录(传递两个目录fuse会出错)。

     

    问题:那么fuse的mount机制到底需要几个目录参数?为什么bbfs要使用两个目录参数?

    答:fuse的mount应该只需要一个目录参数,即挂载点。bbfs在调用fuse_main之前将第一个目录参数进行了处理,保存在一个的结构中,并将这个结构传递给fuse_main。而ssfs调用fuse_main时对应的参数置为NULL。

    fuse_main(argc, argv, &bb_oper, bb_data);

    fuse_main( argc, argv, &operations, NULL );

    问题:这个参数是如何被fuse处理的?

     答:fuse_main的四个参数首先在fuse.h中被扩展为5个参数 fuse_main_real(argc, argv, op, sizeof(*(op)), user_data)

    然后在fuse_main_real中被传递给fuse_main_common,变为6个参数,return fuse_main_common(argc, argv, op, op_size, user_data, 0);

     

    问题:能不能将/dev/sdx1挂在在某个目录下呢?

     

    使用 -f参数时,主程序会在前台运行,阻塞在fuse_main的调用这里,ctrl+c后就会返回,打印后面的语句。

    如果不使用-f参数,运行主程序后就会结束,但是fuse_main调用后面的打印语句并不会打印。

    问题:fuse_main的调用后面到底发生了什么?为什么不是用-f参数主程序看起来是跳过打印语句退出?

     需要进入fuse_main_common查看。在这里,如果不是用-f参数,主程序显示运行到fuse_setup_common之前,如果使用-f参数,主程序会运行到 fuse_setup_common 之后,且fuse=0x189e290, multithreaded=1,进入res = fuse_loop_mt(fuse); ctrl+c后运行fuse_teardown_common对挂载点进行了卸载。

    所以需要进入fuse_setup_common,以及fuse_loop_mt。这两个函数一个用户-f参数下的循环,另一个用于非-f参数下的循环。可以预料,-f参数在fuse_setup_common被处理。

    在fuse_setup_common中会进行mount操作,ch = fuse_mount_common(*mountpoint, &args); 然乎调用 fuse = fuse_new_common(ch, &args, op, op_size, user_data, compat); 这个函数在fuse.c中,现在终于从helper.c进入了fuse.c

    问题

     

     

    在bbfs中,传递的参数有两个目录,fuse将一个目录挂载在另一个目录下。

    在ssfs中,传递的参数只有一个目录(传递两个目录fuse会出错)。

    问题:那么fuse的mount是怎么处理参数的呢?

    问题:能不能将/dev/sdx1挂在在某个目录下呢?

     

    可以运行ssfs两次,挂载不同的目录,运行mount命令显示有两个ssfs。可以使用sudo umount ssfs卸载这个挂载,卸载的是最后一个。

    root@river:/home/leveldb/store# mount

    /dev/sdf1 on /home/leveldb/store/f1 type ext4 (rw)

    bbfs on /home/leveldb/fuse/bbfs/dir2 type fuse.bbfs (rw,nosuid,nodev,user=leveldb)
    ssfs on /home/leveldb/fuse/ssfs/mnt type fuse.ssfs (rw,nosuid,nodev,user=leveldb)
    ssfs on /home/leveldb/fuse/ssfs/dir1 type fuse.ssfs (rw,nosuid,nodev,user=leveldb)

    也可以打印/etc/mtab来查看mount列表

    root@river:/home/leveldb/store# cat /etc/mtab

    /dev/sdf1 /home/leveldb/store/f1 ext4 rw 0 0
    bbfs /home/leveldb/fuse/bbfs/dir2 fuse.bbfs rw,nosuid,nodev,user=leveldb 0 0
    ssfs /home/leveldb/fuse/ssfs/mnt fuse.ssfs rw,nosuid,nodev,user=leveldb 0 0
    ssfs /home/leveldb/fuse/ssfs/dir1 fuse.ssfs rw,nosuid,nodev,user=leveldb 0 0

    这与一般的挂载不一样,一般的挂载比如 mount /dev/sdc1 /mnt/c1ext4 后,/dev/sdc1出现在mount列表的entry中的第一个项,同上述列表中的ssfs。umount的参数可以是第一个项,也可以是第二个项,而fusermount -u 的参数只能是第二个项即挂载点。

    问题:fuse的mount的怎么写入mount列表的?

     

    转载于:https://www.cnblogs.com/bettersky/p/6739667.html

    更多相关内容
  • fusemount –u /home/user/code 五、sshfs选项 general options:  -o opt,[opt...] mount options  -h --help print help  -V --version print version   SSHFS options:  -p PORT equivalent to '-o...

    Ubuntu下使用sshfs挂载远程目录到本地

    访问局域网中其他Ubuntu机器,在不同机器间跳来跳去,很是麻烦,如果能够把远程目录映射到本地无疑会大大方面使用,就像Windows下的网络映射盘一样。在Linux的世界无疑也会有这种机制和方式,最近在使用的过程中选择了sshfs这个工具来达到把远程目录映射到本地的功能。

    SSHFS可在本地安装的文件系统中,通过SSH获得所有加密的通信优势。使用sshfs可以把远程目录直接映射到本地,无需修改远程机器的设置,仅要求有ssh连接的权限(ssh都没有的话,还能干啥?)

    官网地址: http://fuse.sourceforge.net/sshfs.html 

    一、SSHFS for Ubuntu
    首先简单介绍一下SSHF。

    关于SSH的介绍和安装使用可参考:SSH简介及工作机制以及Ubuntu环境下SSH的安装及使用这两篇文章。

    SSH 是一个强大且安全的工具,我们除了可以用它来远程管理主机外,还可以通过它建立 SSH tunnel作 Proxy用,远程传输文件等等。而这里我想要介绍另外一个功能,那就是结合 sshfs 这个工具可以把远程主机的文件系统映射到本地主机上,透过 SSH 把远程文件系统挂载到本机上,这样我们可以不必使用 scp 工具就可以做到直接复制及删除远程主机的文件了,就像操作本地磁盘一样方便。

    SSH是连接两台独立计算机,进行远程控制任务的受信任的方式。

    SSHFS最炫的地方在于可在本地安装的文件系统中,通过SSH获得所有加密的通信优势。sshfs 是基于 FUSE 构建的 SSH 文件系统客户端程序,通过它远程主机的配置无需作任何改变,就可以透过 SSH 协议来挂载远程文件系统了,非常方便及安全。

    二、Ubuntu 上安装sshfs
    Ubuntu源中已经包含了sshfs,以及所需的fuse包,直接安装即可:

    sudo apt-get install sshfs

    linux:

    swupd bundle-add  storage-utils

    安装的时候会自动创建用户组fuse,要使用sshfs的用户需要先加到这个用户组里才行(记得完成后还要重新登录),不然会没有权限执行fusermount:

    fuse: failed to exec fusermount: Permission denied

    三、挂载远程文件系统/目录
    挂载的一般格式为:


    sudo sshfs {{user id}}@{{server hostname}}:{{desiredremote share}} {{desired local mount point}} -o idmap=user -o allow_other -ouid={{local user id}} -o gid={{local group id}}

    其中重点选项:

    -o transform_symlinks 表示转换绝对链接符号为相对链接符号

    -o follow_symlinks 沿用服务器上的链接符号

    -C 压缩,或者-o compression=yes

    -o reconnect 自动重连

    -o cache=yes

    -o allow_other

    挂载格式看起来比较头晕,这里结合我的使用给出一个简洁直观的格式:

    sshfs -o transform_symlinks -ofollow_symlinks  user@hostname: [dir]mountpoint

    实例如下:

    sshfs –o cache=yes,allow_other user@192.168.1.200:/home/user/code home/user/code

    说明:

    1、 连接的时候可能需要管理员权限,在挂载命令前sudo即可。

    2、更多参数请 man sshfs ,如果碰到其它问题请参考官方的 FAQ 。

    3、如果想不输入密码的话,可以使用 SSH key 认证方式。

    4、执行命令后 Ubuntu 会自动在桌面上显示挂载的 sshfs 文件系统盘,现在你就可以像操作本地磁盘一样操作这个映射的磁盘了。

    四、卸载
    fusemount –u /home/user/code

    五、sshfs选项
    general options:
        -o opt,[opt...]        mount options
        -h   --help            print help
        -V   --version         print version
     
    SSHFS options:
        -p PORT                equivalent to '-o port=PORT'
        -C                     equivalent to '-o compression=yes' #启用压缩,建议配上
        -F ssh_configfile      specifies alternative ssh configuration file #使用非默认的ssh配置文件
        -1                     equivalent to '-o ssh_protocol=1' #不要用啊
        -o reconnect           reconnect to server               #自动重连
        -o delay_connect       delay connection to server
        -o sshfs_sync          synchronous writes
        -o no_readahead        synchronous reads (no speculative readahead) #提前预读
        -o sshfs_debug         print some debugging information
        -o cache=BOOL          enable caching {yes,no} (default: yes) #能缓存目录结构之类的信息
        -o cache_timeout=N     sets timeout for caches in seconds (default: 20)
        -o cache_X_timeout=N   sets timeout for {stat,dir,link} cache
        -o workaround=LIST     colon separated list of workarounds
                 none             no workarounds enabled
                 all              all workarounds enabled
                 [no]rename       fix renaming to existing file (default: off)
                 [no]nodelaysrv   set nodelay tcp flag in sshd (default: off)
                 [no]truncate     fix truncate for old servers (default: off)
                 [no]buflimit     fix buffer fillup bug in server (default: on)
        -o idmap=TYPE          user/group ID mapping, possible types are:  #文件权限uid/gid映射关系
                 none             no translation of the ID space (default)
                 user             only translate UID of connecting user
        -o ssh_command=CMD     execute CMD instead of 'ssh'
        -o ssh_protocol=N      ssh protocol to use (default: 2) #肯定要2的
        -o sftp_server=SERV    path to sftp server or subsystem (default: sftp)
        -o directport=PORT     directly connect to PORT bypassing ssh
        -o transform_symlinks  transform absolute symlinks to relative
        -o follow_symlinks     follow symlinks on the server
        -o no_check_root       don't check for existence of 'dir' on server
        -o password_stdin      read password from stdin (only for pam_mount)
        -o SSHOPT=VAL          ssh options (see man ssh_config)
     
    Module options:
     
    [subdir]
        -o subdir=DIR       prepend this directory to all paths (mandatory)
        -o [no]rellinks     transform absolute symlinks to relative
     
    [iconv]
        #字符集转换,对我这种UTF8控,默认已经是最好的
        -o from_code=CHARSET   original encoding of file names (default: UTF-8)
        -o to_code=CHARSET      new encoding of the file names (default: UTF-8)

    六、总结:
    在Ubuntu环境下,直接访问网络上其他电脑的文件时由于权限问题,会出现很多问题;最近特别突出的是远程电脑和本地的文件比较,文件夹比较的时候,不能使用工具,极度不方便。使用sshsf将远程电脑上的文件夹挂载到本地以后,不但可以很方便的访问远程文件而且也能使用比较工具将远程文件/文件夹和本地文件/文件夹进行比较。

    比如使用beyond Compare时不能将远程文件、文件夹和本地的进行比较。挂载远程文件夹之后,此问题迎刃而解。
     

    展开全文
  • 经过上一篇的分析,目前已经知道mount函数最终进入到mount.c 中的int fuse_kern_mount(const char *mountpoint, struct fuse_args *args) 而主题函数进入到fuse.c中的 fuse_new_common 这两个函数都会在helper.c...

    经过上一篇的分析,目前已经知道mount函数最终进入到mount.c 中的 int fuse_kern_mount(const char *mountpoint, struct fuse_args *args)

    而主题函数进入到fuse.c中的 fuse_new_common

     

    这两个函数都会在helper.c中的fuse_setup_common中返回,返回后进入helper.c的 fuse_daemonize 。fuse_daemonize使用 foreground参数也即-f参数。

    如果-f参数为真,则进入fuse_daemonize的循环体,否则fuse_daemonize直接返回,然后fuse_setup_common也返回到fuse_main_common,进入fuse_loop_mt循环。

     

    本篇主要研究fuse_kern_mount函数。

    1. 该函数调用栈:

        fuse_setup_common-> 

            fuse_mount_common(const char *mountpoint,struct fuse_args *args) -> 

             fuse_mount_compat25(const char *mountpoint, struct fuse_args *args)  -> 

            fuse_kern_mount(const char *mountpoint, struct fuse_args *args)

    2.  mountpoint就是命令行提供的目录参数。args也是根据命令行的参数生成的,struct fuse_args args = FUSE_ARGS_INIT(argc, argv);

    #define FUSE_ARGS_INIT(argc, argv) { argc, argv, 0 }

    在fuse_opt.h

    struct fuse_args {
      /** Argument count */
      int argc;

      /** Argument vector. NULL terminated */
      char **argv;

      /** Is 'argv' allocated? */
      int allocated;
    };

    也在fuse_opt.h

    注意,在fuse_setup_common中,将命令行的参数进行了规整,处理出mountpoint和args。mountpoint是挂载点的全路径字符串. 如果命令行是./ssfs dir1 -f , args含有两个字符串,第一个字符串是 ./ssfs,第二个字符串是-osubtype=ssfs

     

    3. fuse_kern_mount首先构建struct mount_opts mo; 

    struct mount_opts {
    int allow_other;
    int allow_root;
    int ishelp;
    int flags;
    int nonempty;
    int auto_unmount;
    int blkdev;
    char *fsname;
    char *subtype;
    char *subtype_opt;
    char *mtab_opts;

    char *fusermount_opts;
    char *kernel_opts;
    };

     然后调用 fuse_mount_sys(mountpoint, &mo, mnt_opts);函数,这里因为 mo->auto_unmount为真,该函数将返回-2,即fallback to fusermount ,

    转而调用fuse_mount_fusermount,这里会调用第一个,并返回3.

    fuse_mount_fusermount(mountpoint, &mo, tmp_opts, 1);

    注意:fuse_mount_sys中主要是open了/dev/fuse以及使用了mount系统调用。 fuse_mount_fusermount中主要使用 socketpair系统调用。

    注意2:fuse_mount_fusermount使用了fork,然后子线程做了一系列操作后_exit了。一个重要操作是 exec_fusermount(argv); argv里包含mountpint以及mount程序名即fusermount。

    static void exec_fusermount(const char *argv[])
    {
      execv(FUSERMOUNT_DIR "/" FUSERMOUNT_PROG, (char **) argv);
      execvp(FUSERMOUNT_PROG, (char **) argv);
    }

    FUSERMOUNT_DIR打印字符串显示的是 /usr/local/bin

    FUSERMOUNT_DIR "/" FUSERMOUNT_PROG打印字符串显示的是/usr/local/bin/fusermount

    原来c语言里将几个字符串直接放在一起就是连起来!

     从这里看来,mount函数最终调用了 fusermount程序,一个自带main函数的程序。该程序的源代码就是 util目录下的fusermount.c

     

    注意:后入如果需要打印到屏幕,需要在fuse_mount_fusermount注销掉改变sdio的代码。

    4. fusermount.c最终调用了系统调用mount。

    调用栈:

    fusermount.c:main ->

      mount_fuse(const char *mnt, const char *opts) ->

      do_mount(const char *mnt, char **typep, mode_t rootmode,

          int fd, const char *opts, const char *dev, char **sourcep, char **mnt_optsp, off_t rootsize) ->

      mount(source, mnt, type, flags, optbuf).

    在mount_fuse向do_mount传递mnt参数之前,调用了check_perm函数,该函数使用chdir将当前的工作路径改为mnt,然后使用"."即当前路径作为mnt。

    后面的几个函数均使用"."作为mnt。

    问题:自己写的程序按照上述的方式使用root调用mount,会出现errno=22即EINVAL source had an invalid superblock. 那么,为什么fuse就可以这样掉用mount呢?

    经验证,在fuse的mount调用中,可以随意改变source,type,flags变量。改变source和type有时会反映到mount列表,有时却不会。

      也可以随便改变mnt变量,但是必须是一个存在可访问的路径,改变mnt变量不会反映到mount列表。

    注意:貌似这里的mount返回了-1,errno为19表示filesystemtype not configured in the kernel. 但是不影响程序正确执行,而且,strace监测不到这个mount。继续发现,只要type里设置了fuse或fuse.xx就返回0,source可随意设置。 strace追踪不到的原因是mount在fork的子程序里运行的。

    如果使用strace -f可以追中到子进程的mount,然而结果显示Operation not permitted。

    使用sudo的话,实际运行的mount代码是fuse_mount_sys函数里的。

     

    转载于:https://www.cnblogs.com/bettersky/p/6745210.html

    展开全文
  • FUSE原理总结

    2021-05-18 04:10:58
    Fuse是filesystem in user space,一个用户空间的文件系统框架,允许非特权用户建立功能完备的文件系统,而不需要重新编译内核。fuse模块仅仅提供内核模块的入口,而本身的主要实现代码位于用户空间中。对于读写虚拟...

    Fuse是filesystem in user space,一个用户空间的文件系统框架,允许非特权用户建立功能完备的文件系统,而不需要重新编译内核。fuse模块仅仅提供内核模块的入口,而本身的主要实现代码位于用户空间中。对于读写虚拟文件系统来讲,fuse是个很好的选择。fuse包含包含一个内核模块和一个用户空间守护进程,将大部分的VFS调用都委托一个专用的守护进程来处理。

    2

    工作原理

    Fuse用户空间文件系统与真实的文件系统不同,它的supper block, indoe, dentry等都是由内存虚拟而来,具体在物理磁盘上存储的真实文件结构是什么,它不关心,且对真实数据的请求通过驱动和接口一层层传递到用户空间的用户编写的具体实现程序里来,这样就为用户开发自己的文件系统提供了便利,这也就是所谓的“用户空间文件系统”的基本工作理念。

    2.1

    模块架构

    FUSE分为三大模块:

    Ø FUSE内核模块(内核态)

    Ø LibFUSE模块(用户态)

    Ø 用户程序模块(用户态)

    用户程序在用户空间实现LibFUSE库封装的文件系统操作;

    LibFUSE实现文件系统主要框架、对“用户实现的文件系统操作代码“的封装、mount管理、通过字符设备/dev/fuse与内核模块通信;

    FUSE内核模块实现VFS接口(实现fuse文件驱动模块的注册、fuse

    的(虚拟)设备驱动、提供supper block、dentry、inode的维护),接收来至后者的请求,传递给LibFUSE,LibFUSE再传递给我们用户程序的接口进行实现操作。

    图一

    2.1.1

    源代码结构及开发库

    FUSE内核模块

    kernel/inode.c —> 主要完成fuse文件驱动模块的注册,提供对supper

    block的维护函数以及其它(驱动的组织开始文件)

    kernel/dev.c —> fuse 的(虚拟)设备驱动

    kernel/control.c

    —> 提供对于dentry的维护及其它

    kernel/dir.c —> 主要提供对于目录inode索引节点的维护

    kernel/file.c —> 主要提供对于文件inode索引节点的维护

    LibFUSE模块

    lib/helper.c —>

    “fuse_main()”调用的主入口

    lib/fuse_kern_chan.c—>主要实现fuse应用层访问(读写)fuse

    driver的功能

    lib/mount_util.c

    –> 提供mount的基础函数调用

    lib/mount.c —> 主要实现设备的“mount”、“umount”等挂接操作

    lib/mount_bsd.c

    —> “Free bsd”下的“mount”、“umount”实现

    lib/fuse_mt.c —>

    fuse 的mount管理

    lib/fuse.c —> lib库主框架文件,实现了主要框架及对“用户实现的文件系统操作代码“的封装

    lib/fuse_lowlevel.c –> 实现比较底层的函数封装,供fuse.c等使用

    lib/fuse_loop.c

    —> fuse lib循环监视“fuse

    driver”的通信缓存

    lib/fuse_loop_mt.c

    —> 同上

    lib/fuse_session.c

    —> fuse会话管理

    开发库

    include/fuse.h —> the library interface of FUSE (High

    Level)

    include/fuse_common.h —> common

    include/fuse_lowlevel.h —> Lowlevel API

    include/fuse_opt.h —> option parsing interface of FUSE

    2.2

    系统工作流程

    fuse_main(lib/helper.c)–fuse用户空间主函数,用户程序调用它时,fuse_main函数解析相关函数(如mountpoint,multithreaded),并调用fuse_mount函数。调用fuse_new()函数,为fuse文件系统数据分配存储空间。调用fuse_loop()函数实现会话的接受与处理。

    fuse_mount(lib/mount.c)–创建UNIX本地套接口,创建并运行子进程fusermount.并返回fuse模块文件fd给fuse_main()函数。

    fusemount(util/fusermount.c)–确保fuse模块已经加载,通过UNIX套接口返回fuse模块的文件fd给fuse_mount()函数。

    fusermount提供一系列挂载选项,如direct_io(跳过页缓存),allow_root(允许root访问挂载的文件系统),allow_other(允许其他用户访问挂载的文件系统),nonempty(允许把文件系统挂载到非空目录),big_writes(支持大于4k的写操作)等

    fuse_new(lib/fuse.c)–为fuse创建数据结构空间,用来存储文件系统数据。

    fuse_loop(lib/fuse.c)(fuse_loop_mt(lib/fuse_mt.c))–从/dev/fuse读取文件系统调用,调用fuse_operation结构中的处理函数,返回调用结果给/dev/fuse.

    如图二:

    图二

    fuse处理请求的整个流程如图三所示,以unlink操作为例进行说明。其中“>”表示调用,”

    图三

    fuse通过fuse_session_loop(或对应多线程的方法)来启动fuse守护程序,守护程序不断的从/dev/fuse上读取请求,并处理。

    2.3.1

    重要数据结构

    l struct fuse_req(fuse_i.h)

    —> 发往客户端的请求

    struct fuse_req

    {

    struct fuse_ll *f;

    uint64_t unique;

    int ctr;

    pthread_mutex_t lock;

    struct fuse_ctx ctx;

    struct fuse_chan *ch;

    int interrupted;

    union {

    struct {

    uint64_t unique;

    } i;

    struct {

    fuse_interrupt_func_t func;

    void *data;

    } ni;

    } u;

    struct fuse_req *next;

    struct fuse_req *prev;

    };

    l struct fuse_session(fuse_session.c)

    —> 客户端管理会话的结构体,其包含“struct

    fuse_chan”结构

    struct fuse_session

    {

    struct fuse_session_ops op;

    void *data;

    volatile int exited;

    struct fuse_chan *ch;

    };

    l struct fuse_chan(fuse_session.c)

    —> 定义客户端与fuse内核连接隧道的结构体

    struct fuse_chan

    {

    struct fuse_chan_ops op;

    struct fuse_session *se;

    int fd;

    size_t bufsize;

    void *data;

    int compat;

    };

    说明:这里的连接隧道不是什么具体的网络连接,而是客户端通过fuse设备驱

    动来读写设备缓存(以与设备交互的一条概念上的隧道

    l fc数据结构:

    struct

    fuse_conn

    {

    ……….

    wait_queue_head_t

    waitq;

    struct list_head

    pending;

    struct list_head

    processing;

    ……….

    };

    2.3.2

    代码片段1

    //在fuse_main中会被调用,或其多线程版本

    int

    fuse_session_loop(struct fuse_session

    *se)

    {

    int res = 0;

    struct fuse_chan *ch = fuse_session_next_chan(se, NULL);

    size_t bufsize = fuse_chan_bufsize(ch);

    char *buf = (char *) malloc(bufsize); //为channel分配好缓冲区

    if (!buf) {

    fprintf(stderr, “fuse: failed to allocate read

    buffer\n”);

    return -1;

    }

    //fuse daemon, loops

    while (!fuse_session_exited(se)) {

    struct fuse_chan *tmpch = ch;

    //分析见代码片段2,从/dev/fuse读请求,会等待一直到有请求为止

    res = fuse_chan_recv(&tmpch,

    buf, bufsize);

    if (res == -EINTR)

    continue;

    if (res <= 0)

    break;

    fuse_session_process(se, buf, res,

    tmpch); //处理读到的请求

    }

    free(buf);

    fuse_session_reset(se);

    return res < 0 ? -1 : 0;

    }

    2.3.3

    代码片段2

    int

    fuse_chan_recv(struct fuse_chan **chp, char *buf, size_t

    size)

    {

    struct fuse_chan *ch = *chp;

    if (ch->compat)

    return ((struct fuse_chan_ops_compat24 *)

    &ch->op)

    ->receive(ch, buf, size);

    else

    return ch->op.receive(chp,

    buf, size);

    //由下面的一段代码可以发现,receive最终是通过fuse_kern_chan_receive实现的,代码片段3分析该请求

    }

    #define MIN_BUFSIZE

    0×21000

    struct fuse_chan

    *fuse_kern_chan_new(int fd)

    {

    //channel的读写方法

    struct fuse_chan_ops op =

    {

    .receive = fuse_kern_chan_receive,

    .send = fuse_kern_chan_send,

    .destroy = fuse_kern_chan_destroy,

    };

    //设置bufsize大小

    size_t bufsize = getpagesize() + 0×1000;

    bufsize = bufsize < MIN_BUFSIZE ? MIN_BUFSIZE :

    bufsize;

    return fuse_chan_new(&op, fd, bufsize,

    NULL);

    }

    2.3.4

    代码片段3

    static int

    fuse_kern_chan_receive(struct fuse_chan **chp, char

    *buf,

    size_t size)

    {

    struct fuse_chan *ch = *chp;

    int err;

    ssize_t res;

    struct fuse_session *se = fuse_chan_session(ch);

    assert(se != NULL);

    // 一直轮询,直到读到请求为止

    restart:

    //fuse_chan_fs获取到/dev/fuse的文件描述符,调用read系统调用从设备读取请求

    res =

    read(fuse_chan_fd(ch), buf, size);

    //根据fuse设备驱动程序file结构的实现(dev.c),read将调用fuse_dev_read,该方法最终通过fuse_dev_readv实现,根据代码中的注释,fuse_dev_read做了如下工作:

    Read a single request into the userspace

    filesystem’s buffer. This

    function waits until a request is available, then

    removes it from

    the

    pending list and copies request data to userspace

    buffer.

    if no data: goto restart

    ………

    }

    2.3.5

    一阶段小结

    以上的分析对应了fuse filesystem daemon做的第一部分工作。当用户从控制台输入“rm

    /mnt/fuse/file”时,通过VFS(sys_unlink),再到fuse(dir.c中实现的inode_operations,file.c中实现的file_operations中的方法都会最终调用request_send,后面会介绍),这个请求最终被发到了/dev/fuse中,该请求的到达会唤醒正在等待的fuse守护程序,fuse守护程序读取该请求并进行处理,接下来介绍处理请求所作的工作。

    2.3.6

    代码片段4

    struct fuse_session

    *fuse_lowlevel_new_common(struct fuse_args *args,

    const struct fuse_lowlevel_ops

    *op,

    size_t op_size, void *userdata)

    {

    //开发者实现了fuse_lowlevel_ops并传递给fuse_lowlevel_common

    struct fuse_ll *f;

    struct fuse_session *se;

    struct

    fuse_session_ops sop = {

    //最终调用的处理方法

    .process =

    fuse_ll_process, //分析见代码片段5

    .destroy = fuse_ll_destroy,

    };

    …….

    }

    2.3.7

    代码片段5

    static void

    fuse_ll_process(void *data, const char *buf, size_t len,

    struct fuse_chan *ch)

    {

    struct fuse_ll *f = (struct fuse_ll *) data;

    struct fuse_in_header *in = (struct fuse_in_header *)

    buf;

    const void *inarg = buf + sizeof(struct fuse_in_header);

    struct fuse_req

    *req;

    //创建并初始化一个请求

    req = (struct fuse_req *) calloc(1, sizeof(struct

    fuse_req));

    if (req == NULL) {

    fprintf(stderr, “fuse: failed to allocate request\n”);

    return;

    }

    req->f = f;

    req->unique =

    in->unique;

    ……

    //根据opcode调用fuse_ll_ops中相应的方法

    fuse_ll_ops[in->opcode].func(req,

    in->nodeid, inarg);

    }

    }

    2.3.8

    二阶段小结

    以上代码对应中流程中perform unlink的工作,实际上就是执行开发者实现的一组方法来完成相关的工作,接下来就是把执行完请求后需要的数据返回,最终是通过send_reply实现的,

    2.3.9

    代码片段6

    static int

    send_reply(fuse_req_t req, int error, const void *arg,

    size_t argsize)

    {

    struct iovec iov[2];

    int count = 1;

    if (argsize) {

    iov[1].iov_base = (void *) arg;

    iov[1].iov_len = argsize;

    count++;

    }

    return send_reply_iov(req, error, iov,

    count);

    }

    static int

    send_reply_iov(fuse_req_t req, int error, struct iovec

    *iov,

    int count)

    {

    ……

    res = fuse_chan_send(req->ch, iov,

    count);

    free_req(req);

    return res;

    }

    static int

    fuse_kern_chan_send(struct fuse_chan *ch, const struct iovec

    iov[],

    size_t count)

    {

    if (iov) {

    //将数据写到/dev/fuse上,最终会调用fuse_dev_write

    ssize_t res = writev(fuse_chan_fd(ch), iov,

    count);

    ……

    return 0;

    }

    另外fuse接收到VFS的请求时,通过request_send将请求发送到/fuse/dev,并调用request_wait_answer等待返回结果。至于fuse使用的队列的管理,在流程图中也做了简单的说明,下一章将详细分析队列的管理。

    2.3.10

    代码片段7

    {

    req->isreply = 1;

    spin_lock(&fc->lock);

    if (!fc->connected)

    req->out.h.error = -ENOTCONN;

    else if (fc->conn_error)

    req->out.h.error = -ECONNREFUSED;

    else {

    //将请求加入请求队列

    queue_request(fc, req);

    __fuse_get_request(req);

    //等待结果

    request_wait_answer(fc,

    req);

    }

    spin_unlock(&fc->lock);

    }

    2.4

    队列管理

    fuse通过fuse_session_loop来启动守护程序,守护程序最终会调用fuse_dev_readv,fuse_dev_readv调用request_wait,使得进程在fc的waitq队列上睡眠。

    2.4.1

    代码片段1

    static ssize_t

    fuse_dev_readv(struct file *file, const struct iovec

    *iov,

    unsigned long nr_segs, loff_t *off)

    {

    …..

    request_wait(fc);

    ….

    }

    static void

    request_wait(struct fuse_conn *fc)

    {

    //定义一个队列节点变量wait,其与当前进程相关联

    DECLARE_WAITQUEUE(wait, current);

    //将wait加入到fc->waitq等待队列中,当有请求发到fuse文件系统时(通过request_send),这个等待队列上的进程会被唤醒,某一个进程会被赋予CPU使用权

    add_wait_queue_exclusive(&fc->waitq,

    &wait);

    //不断的检查fc的pending队列及interrupts队列,看是否有请求,没有请求会一直while循环

    while (fc->connected

    && !request_pending(fc)) {

    set_current_state(TASK_INTERRUPTIBLE);

    if (signal_pending(current))

    break;

    spin_unlock(&fc->lock);

    schedule(); //选择一个进程运行

    spin_lock(&fc->lock);

    }

    //有请求,将进程设为TASK_RUNNING状态

    set_current_state(TASK_RUNNING);

    //将wait从等待队列中移除

    remove_wait_queue(&fc->waitq,

    &wait);

    }

    static int

    request_pending(struct fuse_conn *fc)

    {

    return !list_empty(&fc->pending) ||

    !list_empty(&fc->interrupts);

    }

    2.4.2

    代码片段2

    request_send是用户请求经过vfs,再到fuse operation中被调用的,它向/dev/fuse发送请求

    void

    request_send(struct fuse_conn *fc, struct fuse_req *req)

    {

    ……

    queue_request(fc,

    req);

    request_wait_answer(fc,

    req);

    ……

    }

    {

    //将请求加入到pending队列

    list_add_tail(&req->list,

    &fc->pending);

    req->state = FUSE_REQ_PENDING;

    if (!req->waiting) {

    req->waiting = 1;

    atomic_inc(&fc->num_waiting);

    }

    //唤醒等待等列

    wake_up(&fc->waitq);

    kill_fasync(&fc->fasync, SIGIO,

    POLL_IN);

    }

    static void

    request_wait_answer(struct fuse_conn *fc, struct fuse_req

    *req)

    {

    //该调用会在req的waitq上睡眠,fuse守护程序处理完请求后,会将其唤醒

    }

    fuse守护程序处理完请求,最终通过fuse_dev_writev写回/dev/fuse,它将唤醒相应req中waitq的等待队列元素,从而让文件系统请求完成request_wait_answer,获取到结果。

    static ssize_t

    fuse_dev_writev(struct file *file, const struct iovec

    *iov,

    unsigned long nr_segs, loff_t *off)

    {

    req = request_find(fc,

    oh.unique);

    request_end(fc, req);

    }

    {

    //唤醒req上的等待队列

    wake_up(&req->waitq);

    }

    fuse设备其的主要工作其实就是进行队列的管理,对fuse设备的读(写)其实就是从相应的队列移除(添加)请求(或响应),request_send将请求加入pending队列,唤醒fuse守护程序,并在req的waitq上等待请求结果,守护程序通过fuse_dev_readv从pending队列中移除请求并处理,处理完成后,守护程序唤醒req的waitq上的进程,该进程读取结果,并返回给用户。总的来说,一个请求从发起到完成会经过4步:

    ü fuse守护程序在fc的waitq上等待请求.

    ü

    fuse deamon process wait

    ü 用户的请求唤醒fc的waitq,从该waitq上移除一个请求进行处理,并在req的waitq上等待请求结果;

    ü fuse守护程序被唤醒,读取请求,处理请求,返回结果,唤醒对应req上的waitq队列。

    ü 请求被唤醒,读取fuse守护程序返回的结果,返回给用户。

    3

    开发API

    4

    参考资料

    展开全文
  • mount系统调用的原型如下 int mount(const char *source, const char *target, const char *filesystemtype, unsigned long mountflags, const void *data); 其中source为设备文件的路径名,target为挂载...
  • Fuse是filesystem in user space,一个用户空间的文件系统框架,允许非特权用户建立功能完备的文件系统,而不需要重新编译内核。fuse模块仅仅提供内核模块的入口,而本身的主要实现代码位于用户空间中。对于读写虚拟...
  • 当挂载路径下已经有这个同名路径时,为了避免冲突,会报这个信息,并且新的文件还挂载不上去。 ...use the 'nonempty' mount option 叫你使用这个参数. 这是我们刘经理给我解决的 ๑乛◡乛๑
  • fuse初使用-eins

    2021-11-10 20:21:31
    fuse的github及介绍 fuse-github fuse-wiki-zh meson;ninja wget + tar 得到fuse源代码(我下载的是3.10.5),发现没有自己熟悉的configure、makefile,傻眼了 一度以为自己下载的压缩包有问题,是不是贡献者忘了?...
  • 此时需要添加参数 挂载失败的命令: curlftpfs -o codepage=gbk ftp://----:-----@--.--.--.--- /--/----/ 挂载成功的命令: ...curlftpfs -o codepage=gbk -o nonempty ftp://----:-----@--.--.--.--- /--/----/...
  • vmware 解决内核启动时mount 根文件系统失败 我的环境是在vmware 下进行的,但解决问题的方法是相通的. 我的问题是scsi 硬盘驱动问题,但纵然是别的问题,也一样可以追踪解决. 错误提示: VFS: Cannot open root device...
  • 报错信息 fuse: failed to exec fusermount: No such file or directory
  • Alluxio FUSE 实现原理

    2021-05-05 01:18:46
    Alluxio FUSE服务通过提供Unix/Linux下的标准POSIX文件系统接口,让应用程序(比如Tensorflow,PyTorch等)在不修改代码的前提下,以访问本地文件系统的方式访问 Alluxio分布式文件系统中的数据。本文介绍Alluxio ...
  • fail to mount mount *.mount

    2021-11-25 18:47:07
    f you deleted snap files manually, for instance, in order to clean up space, some.mountfiles were left in/etc/systemd/system/, like/etc/systemd/system/snap-core20-975.mount. You can try the following...
  • ceph存储 ceph-fuse源码分析一

    千次阅读 2014-12-11 16:59:04
    Fuse Src Analyze 1.前言 本文是对FUSE-2.9.2源码的学习总结。FUSE代码在用户空间和内核空间都有运行,为了突出重点,先简要描述了在基于FUSE的用户空间文件系统中执行write操作的...2.mount和open过程 3.对文件wri
  • Ceph fuse挂载

    2021-08-16 20:27:45
    4. 关于fuse的挂载参数 无配置挂载,指定IP挂载 1. 前提 # 写入keyring到文件 echo -e "[client.admin]\nkey = AQDk18FgMo7NABAA4ufuz3O6/0lE4vsVgHs1yQ==" > /var/cephfs_keyring 2. 命令行 # fuse挂载 ...
  • FUSE概述 FUSE(用户态文件系统)是一个实现在用户空间的文件系统框架,通过FUSE内核模块的支持,使用者只需要根据fuse提供的接口实现具体的文件操作就可以实现一个文件系统。 在fuse出现以前,Linux中的文件系统都...
  • 我正在使用jnr-fuse库(https://github.com/SerCeMan/jnr-fuse)在java中编写Fuse-Filesystem,它在内部使用JNR进行本机访问。文件系统作为Amazon S3存储桶的前端,基本上允许用户将其存储桶作为普通存储设备安装。在...
  • 克隆ubuntu镜像后,打开,报以上错误。 解决办法: 开启共享文件夹:
  • 1.首先确认一下Linux系统内核 [root@.../bin/bash mount /dev/sda5 /media/disk_d -t ntfs-3g 保存文件,修改权限: #chmod u+x mount 在/etc/rc.d/rc.local中添加如下一行: sh ./home/mount 下次开机就能自动挂载了。
  • 说明FUSE 是Linux Kernel的特性之一:一个用户态文件系统框架,a userspace filesystem framework。 形象的说就是可以在用户态运行一个程序,这个程序暴露出一个FUSE文件系统,对这个文件系统进行的读写操作都会被转...
  • FUSE源码剖析

    千次阅读 2016-05-05 17:45:39
    FUSE代码在用户空间和内核空间都有运行,为了突出重点,先简要描述了在基于FUSE的用户空间文件系统中执行write操作的一般流程,接下来介绍了重要的数据结构,最后以FUSE的运行过程为线索,剖析FUSE程序运行过程的3个...
  • FUSE 简介

    千次阅读 2015-04-21 22:10:11
    FUSE的工作原理如图所示。假设基于FUSE的用户态文件系统hello挂载在/tmp/fuse目录下。当应用层程序要访问/tmp/fuse下的文件时,通过glibc中的函数进行系统调用,处理这些系统调用的VFS中的函数会调用FUSE在内核中的...
  • 需要加速人工智能云上分析(如TensorFlow本地训练,可通过FUSE挂载Alluxio FS到本地) 我也做了很多Allxuio的性能测试工作,效果都不是很理想,有幸与Alluxio PMC范斌和李浩源交流了测试结果不如人意的原因,大佬是这么...
  • VMware无法通过mount挂载共享文件夹

    千次阅读 2017-09-01 17:57:13
    ubuntu:虚拟机安装共享目录的时候以前的做法://1、安装: ...sudo mount -t vmhgfs .host:/ /mnt/hgfs现在需要改变的地方://命令用错了,安装了open vm tools以后要用: vmhgfs-fuse .host:/ /m
  • Ubuntu 无法mount解决办法: mount:cannot mount block device //10.0.0.2/ming read-only 解决办法: $ sudo apt-get install cifs-utils (或下载下来deb包安装) 也可直接用下面方法解决(即mount命令中加入sec=...
  • mount.pl:通过FUSE和SSHFS处理远程文件系统的挂载和卸载
  • go-fuse:Go的FUSE绑定

    2021-04-28 02:00:26
    FUSE内核模块进行本机绑定。 您应该导入并使用库。 它紧密遵循有线协议,但为构建基于节点和路径的文件系统提供了方便的抽象方法 可在和上获得旧的,已弃用的API。 与其他FUSE库的比较 FUSE库在2019年完成的重写...
  • 深入理解GlusterFS之POSIX接口

    万次阅读 2017-09-01 19:34:31
    剖析GlusterFS基于FUSE的POSIX文件系统接口的实现机制和工作原理,给出提升大I/O带宽性能的具体方法,并提出FUSE进一步的优化思路。
  • apfs-fuse:APFS的FUSE驱动程序(苹果文件系统)
  • 当需要挂载的目录下不为空时,...fuse: mountpoint is not empty fuse: if you are sure this is safe, use the 'nonempty' mount option产生这种问题的原因是因为如果挂载目录下的文件名和挂载后的产生的文件名如果

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 10,254
精华内容 4,101
关键字:

fusemount