无需进行内核编程

使用用户空间的文件系统(FUSE),您无需理解文件系统的内幕,也不用学习内核模块编程的知识,就可以开发用户空间的文件系统框架。本文是一篇简单的逐步介绍的指南,内容包括安装、定制和启用 FUSE 和 AFS,这样您就可以在 Linux® 的用户空间中创建自己的功能完备的文件系统了。

1 评论

Sumit Singh (sumising@in.ibm.com), 软件工程师, IBM

2006 年 4 月 10 日

  • 内容

在 IBM Bluemix 云平台上开发并部署您的下一个应用。

开始您的试用

文件系统是一种用来存储和组织计算机文件、目录及其包含的数据的方法,它使文件、目录以及数据的查找和访问得到简化。如果您正在使用一台计算机,很可能使用了多个文件系统。文件系统能提供丰富的扩展能力。它可以编写成底层文件系统的一个封装程序,从而对其中的数据进行管理,并提供一个增强的、具有丰富特性的文件系统(例如 cvsfs-fuse,它为 CVS 提供了一个文件系统的接口;或 Wayback 文件系统,它提供了一种用于保留原始数据文件的文件备份机制)。

在用户空间的文件系统出现之前,文件系统的开发曾是内核开发人员的工作。创建文件系统需要了解内核编程和内核技术(例如 vfs)方面的知识。调试则需要 C 和 C++ 方面的专业技能。但是其他开发人员需要熟练地操作文件系统以添加个性化特性(例如添加历史记录或转发缓存)及对其改进。

FUSE 简介

使用 FUSE 您可以开发功能完备的文件系统:其具有简单的 API 库,可以被非特权用户访问,并可以安全的实施。更重要的是,FUSE 以往的表现充分证明了其稳定性。

使用 FUSE,您可以像可执行二进制文件一样来开发文件系统,它们需要链接到 FUSE 库上 —— 换言之,这个文件系统框架并不需要您了解文件系统的内幕和内核模块编程的知识。

就文件系统来说,用户空间的文件系统就不再是新奇的设计了。用户空间文件系统的商业实现与学术实现的实例包括:

  • LUFS 是一个混合用户空间的文件系统框架,它对用于任何应用程序无数的文件系统提供透明支持。大部分LUFS 包括一个内核模块和一个用户空间的守护进程。从根本上来说,它将大部分 VFS 调用都委托给一个专用的守护进程来处理。
  • UserFS 让用户进程可以像普通的文件系统一样进行加载。这种概念性的原型提供了 ftpfs,这可以使用文件系统接口提供匿名 FTP 访问。
  • Ufo Project 是为 Solaris 提供的一个全局文件系统,它允许用户将远程文件真正当作本地文件一样对待。
  • OpenAFS 是 Andrew FileSystem 的一个开源版本。
  • CIFS 是 Common Internet FileSystem 的简称。

与这些商业实现和学术实现不同,FUSE 将这种文件系统的设计能力带到了 Linux 中来。由于 FUSE 使用的是可执行程序(而不像 LUFS 一样使用的是共享对象),因此可以简化程序的调试和开发。FUSE 可以在 2.4.x 和 2.6.x 的内核上使用,现在可以支持 Java™ 绑定,因此您可以不必限定于使用 C 和 C++ 来编写文件系统了。(有关更多使用 FUSE 的用户层的文件系统的内容,请参阅 参考资料。)

要在 FUSE 中创建一个文件系统,您需要安装一个 FUSE 内核模块,然后使用 FUSE 库和 API 来创建自己的文件系统。

回页首

展开 FUSE

要开发一个文件系统,首先请下载 FUSE 的源代码(请参阅 参考资料)并展开这个包:tar -zxvf fuse-2.2.tar.gz。这会创建一个 FUSE 目录,其中保存的是源代码。fuse-2.2 目录的内容如下:

  • ./doc 包含了与 FUSE 有关的文档。现在,这只有一个文件 how-fuse-works。
  • ./kernel 包含了 FUSE 内核模块的源代码(对于使用 FUSE 开发文件系统来说,您当然不用懂得这些代码的机制)。
  • ./include 包含了 FUSE API 头,您需要这些文件来创建文件系统。您现在唯一需要的就是 fuse.h。
  • ./lib 中存放的是创建 FUSE 库的源代码,您需要将它们与您的二进制文件链接在一起来创建文件系统。
  • ./util 中存放的是 FUSE 工具库的源代码。
  • ./example 当然包含的是一些供您参考的例子,例如 fusexmp.null 和 hello 文件系统。

回页首

编译并安装 FUSE

  1. 在 fuse-2.2 目录中运行 configure 脚本: ./configure。这会创建所需要的 makefile 等内容。
  2. 运行 ./make 来编译库、二进制文件和内核模块。查看 kernel 目录中的文件 ./kernel/fuse.ko —— 这是内核模块文件。还要查看 lib 目录中的 fuse.o、mount.o 和 helper.o。
  3. 运行 ./make install 完成 FUSE 的安装。 

    另外一种选择:如果您希望使用 insmod 自己将这个模块安装到内核中,就可以跳过这个步骤。例如:/usr/local/sbin/insmod ./kernel/fuse.ko 或 /sbin/insmod ./kernel/fuse.ko。记住要使用 root 特权才能安装所需要的模块。

如果希望,只在一个步骤中就可以完成上面的步骤。在 fuse-2.2 目录中,运行 ./configure; make; make install;

重要提示:在编译 FUSE 时,系统中需要有内核头文件或源代码。为了简单起见,请确保将内核源代码放到 /usr/src/ 目录中。

回页首

定制文件系统

现在让我们来创建一个文件系统,这样就可以使用一个较旧的 Linux 内核来访问一个具有最新内核的 Linux 系统上的 AFS 空间了。您需要两个进程:一个是在较旧的 Linux 内核上运行的服务器进程,另外一个是在具有最新内核的 Linux 系统上运行的一个 FUSE 客户机进程。不论何时请求到达您的 FUSE 客户机进程上时,它都会与远程服务器进程进行联系。为了进行通信,这个文件系统使用了 RX RPC 代码,这是 AFS 的一部分,因此您需要编译 OpenAFS。(图 1 给出了对这个 AFS 文件系统的概述。)

图 1. AFS-FUSE 文件系统概述

编译 OpenAFS

  1. 下载 OpenAFS Linux 源代码并展开源代码。

    在展开源代码的目录中,运行 ./make ./configure --enable-transarc-paths。如果 ./configure 无法理解编译使用的 sysname,就请使用 --with-afs-sysname 选项提供正确的 sysname。

    要在 Linux 2.4 内核上编译,请使用下面的命令:./configure --enable-transarc-paths --with-afs-sysname=i386_linux24

  2. 运行 ./make,然后运行 ./make dest。检查在编译过程中出现的错误。

    如果编译过程一切顺利,那么 AFS 源代码树就可以使用了。现在,您需要准备一个开发目录 afsfuse。在这个目录中,创建另外两个目录:

    • include 目录包括 OpenAFS 和 FUSE 的 include 目录的头文件。
    • lib 目录包含 OpenAFS 和 FUSE 的库文件。
  3. 拷贝头文件和库文件。

    首先从 OpenAFS 目录中拷贝 AFS 的头文件,方法是将 dest\i386_linux24\include 中的目录和文件全部拷贝到 include 目录中。然后将 fuse-2.2 目录中的 FUSE 的 include 目录拷贝到这个目录中。对库文件也重复相同的步骤,将它们全部拷贝到 lib 目录中。

  4. 创建应用程序的结构。

    对于这两组进程,您需要使用两组文件。使用命名规则 afsfuse_client.* 来命名客户机进程的文件;使用命名规则 afsfuse_server.* 来命名服务器进程的文件。

    这样您就有了一个 afsfuse_client.c 文件,其中包含了 FUSE 进程的代码;一个 afsfuse_server.c 文件,其中包含了在远程机器上运行的进程使用的服务器代码;一个 makefile;一个 rxgen 文件,用来创建 RPC 头文件(例如 afsfuse.xg)。

    afsfuse_client.c 文件可以创建 afsfuse_client 进程代码, FUSE 文件系统调用它来创建文件系统(使用 fuse-2.2/example/fusexmp.c 来创建这个文件)。

定义需要的函数

要使用 FUSE 来创建一个文件系统,您需要声明一个 fuse_operations 类型的结构变量,并将其传递给 fuse_main 函数。fuse_operations 结构中有一个指针,指向在执行适当操作时需要调用的函数。清单 1 给出了 fuse_operations 结构。

清单 1. fuse_operation 结构中需要的函数
struct fuse_operations {int (*getattr) (const char *, struct stat *);int (*readlink) (const char *, char *, size_t);int (*getdir) (const char *, fuse_dirh_t, fuse_dirfil_t);int (*mknod) (const char *, mode_t, dev_t);int (*mkdir) (const char *, mode_t);int (*unlink) (const char *);int (*rmdir) (const char *);int (*symlink) (const char *, const char *);int (*rename) (const char *, const char *);int (*link) (const char *, const char *);int (*chmod) (const char *, mode_t);int (*chown) (const char *, uid_t, gid_t);int (*truncate) (const char *, off_t);int (*utime) (const char *, struct utimbuf *);int (*open) (const char *, struct fuse_file_info *);int (*read) (const char *, char *, size_t, off_t, struct fuse_file_info *);int (*write) (const char *, const char *, size_t, off_t,struct fuse_file_info *);int (*statfs) (const char *, struct statfs *);int (*flush) (const char *, struct fuse_file_info *);int (*release) (const char *, struct fuse_file_info *);int (*fsync) (const char *, int, struct fuse_file_info *);int (*setxattr) (const char *, const char *, const char *, size_t, int);int (*getxattr) (const char *, const char *, char *, size_t);int (*listxattr) (const char *, char *, size_t);int (*removexattr) (const char *, const char *);
};

这些操作并非都是必需的,但是一个文件系统要想正常工作,就需要其中的很多函数。您可以实现一个具有特殊目的的 .flush.release 或.fsync 方法的功能完备的文件系统。(本文不会介绍任何 xattr 函数。)清单 1 中给出的函数如下所示:

  • getattr: int (*getattr) (const char *, struct stat *);
    这个函数与 stat() 类似。st_dev 和 st_blksize 域都可以忽略。st_ino 域也会被忽略,除非在执行 mount 时指定了 use_ino 选项。
  • readlink: int (*readlink) (const char *, char *, size_t);
    这个函数会读取一个符号链接的目标。缓冲区应该是一个以 null 结束的字符串。缓冲区的大小参数包括这个 null 结束字符的空间。如果链接名太长,不能保存到缓冲区中,就应该被截断。成功时的返回值应该是 “0”。
  • getdir: int (*getdir) (const char *, fuse_dirh_t, fuse_dirfil_t);
    这个函数会读取一个目录中的内容。这个操作实际上是在一次调用中执行 opendir()readdir()、...、closedir() 序列。对于每个目录项来说,都应该调用 filldir() 函数。
  • mknod: int (*mknod) (const char *, mode_t, dev_t);
    这个函数会创建一个文件节点。此处没有 create() 操作;mknod() 会在创建非目录、非符号链接的节点时调用。
  • mkdir: int (*mkdir) (const char *, mode_t);
    rmdir: int (*rmdir) (const char *);
    这两个函数分别用来创建和删除一个目录。
  • unlink: int (*unlink) (const char *);
    rename: int (*rename) (const char *, const char *);
    这两个函数分别用来删除和重命名一个文件。
  • symlink: int (*symlink) (const char *, const char *);
    这个函数用来创建一个符号链接。
  • link: int (*link) (const char *, const char *);
    这个函数创建一个到文件的硬链接。
  • chmod: int (*chmod) (const char *, mode_t);
    chown: int (*chown) (const char *, uid_t, gid_t);
    truncate: int (*truncate) (const char *, off_t);
    utime: int (*utime) (const char *, struct utimbuf *);
    这 4 个函数分别用来修改文件的权限位、属主和用户、大小以及文件的访问/修改时间。
  • open: int (*open) (const char *, struct fuse_file_info *);
    这是文件的打开操作。对 open() 函数不能传递创建或截断标记(O_CREATO_EXCLO_TRUNC)。这个函数应该检查是否允许执行给定的标记的操作。另外,open() 也可能在 fuse_file_info 结构中返回任意的文件句柄,这会传递给所有的文件操作。
  • read: int (*read) (const char *, char *, size_t, off_t, struct fuse_file_info *);
    这个函数从一个打开文件中读取数据。除非碰到 EOF 或出现错误,否则 read() 应该返回所请求的字节数的数据;否则,其余数据都会被替换成 0。一个例外是在执行 mount 命令时指定了 direct_io 选项,在这种情况中 read() 系统调用的返回值会影响这个操作的返回值。
  • write: int (*write) (const char *, const char *, size_t, off_t, struct fuse_file_info *);
    这个函数将数据写入一个打开的文件中。除非碰到 EOF 或出现错误,否则 write() 应该返回所请求的字节数的数据。一个例外是在执行 mount 命令时指定了 direct_io 选项(这于 read() 操作的情况类似)。
  • statfs: int (*statfs) (const char *, struct statfs *);
    这个函数获取文件系统的统计信息。f_type 和 f_fsid 域都会被忽略。
  • flush: int (*flush) (const char *, struct fuse_file_info *);
    这表示要刷新缓存数据。它并不等于 fsync() 函数 —— 也不是请求同步脏数据。每次对一个文件描述符执行 close() 函数时,都会调用flush();因此如果文件系统希望在 close() 中返回写错误,并且这个文件已经缓存了脏数据,那么此处就是回写数据并返回错误的好地方。由于很多应用程序都会忽略 close() 错误,因此这通常用处不大。

注意:我们也可以对一个 open() 多次调用 flush() 方法。如果由于调用了 dup()dup2() 或 fork() 而产生多个文件描述符指向一个打开文件的情况,就可能会需要这种用法。我们无法确定哪个 flush 操作是最后一次操作,因此每个 flush 都应该同等地对待。多个写刷新序列相当罕见,因此这并不是什么问题。

  • release: int (*release) (const char *, struct fuse_file_info *);
    这个函数释放一个打开文件。release() 是在对一个打开文件没有其他引用时调用的 —— 此时所有的文件描述符都会被关闭,所有的内存映射都会被取消。对于每个 open() 调用来说,都必须有一个使用完全相同标记和文件描述符的 release() 调用。对一个文件打开多次是可能的,在这种情况中只会考虑最后一次 release,然后就不能再对这个文件执行更多的读/写操作了。release 的返回值会被忽略。
  • fsync: int (*fsync) (const char *, int, struct fuse_file_info *);
    这个函数用来同步文件内容。如果 datasync 参数为非 0,那么就只会刷新用户数据,而不会刷新元数据。
  • setxattr: int (*setxattr) (const char *, const char *, const char *, size_t, int);
    getxattr: int (*getxattr) (const char *, const char *, char *, size_t);
    listxattr: int (*listxattr) (const char *, char *, size_t);
    removexattr: int (*removexattr) (const char *, const char *);
    这些函数分别用来设置、获取、列出和删除扩展属性。

获得的文件系统

您的文件系统将如下所示:

afsfuse_client   <--RX[RPC]-->   afsfuse_server

afsfuse_client 会将传递给它的文件系统调用转发给另外一台机器上的 afsfuse_server。afsfuse_server 会对客户机传递给它的所有请求进行处理,并将结果返回给客户机。它会执行所有必需的工作。RPC 使用的机制是 RX。数据或原数据都不会涉及缓存的问题。

回页首

定义 RX RPC 层

在继续之前,您需要先定义 RX RPC 层。要实现这种功能,请为 rxgen 创建一个 .xg 文件,用来描述代理和与 afsfuse_client.c 和 afsfuse_server.c 进行链接的桩代码。清单 2 显示了如何创建一个具有如下内容的 afsfuse.xg 文件:

清单 2. 创建 afsfuse.xg 文件
#define MYMAXPATH 512
%#include <rx/rx.h>
%#include </rx_null.h >
%#define SAMPLE_SERVER_PORT 5000
%#define SAMPLE_SERVICE_PORT 0
/* i.e. user server's port */
%#define SAMPLE_SERVICE_ID 4 /* Maximum number of requests that will be handled by thisservice simultaneously */
/* This number will also be guaranteed to execute in parallel if no services' requestsare being processed */
%#define SAMPLE_MAX 2 /* Minimum number of requests that are guaranteed to be handledimmediately */
%#define SAMPLE_MIN 1 /* Index of the "null" security class in the sample service. Thismust be 0 (there are N classes, numbered from 0. In this case,N is 1) */
%#define SAMPLE_NULL 0 /********************** fuse4_file_info taken from fuse.h therxgen does not understands fuse.h  mystat taken from man 2mystat these are required again rxgen does not understand thestruct paras and will bump.. **********************/
struct my_file_info { /** Open flags. Available in open() and release() */int flags; /** File handle. May be filled in by filesystem inopen(). Available in all other file operations */unsigned int fh; /** In case of a write operation indicates ifthis was caused by a writepage */int writepage;};
struct mystatfs {afs_uint32 f_type; /* type of filesystem (see below) */afs_uint32 f_bsize; /* optimal transfer block size */afs_uint32 f_blocks; /* total data blocks in file system */afs_uint32 f_bfree; /* free blocks in fs */afs_uint32 f_bavail; /* free blocks avail to non-superuser */afs_uint32 f_files; /* total file nodes in file system */afs_uint32 f_ffree; /* free file nodes in fs */afs_uint32 f_fsid1; /* file system id */afs_uint32 f_fsid2; /* file system id */afs_uint32 f_namelen; /* maximum length of filenames */afs_uint32 f_spare[6]; /* spare for later */};
struct mystat {afs_uint32 st_dev; /* device */afs_uint32 st_ino; /* inode */afs_uint32 st_mode; /* protection */afs_uint32 st_nlink; /* number of hard links */afs_uint32 st_uid; /* user ID of owner */afs_uint32 st_gid;/* group ID of owner */afs_uint32 st_rdev; /* device type (if inode device) */afs_uint32 st_size; /* total size, in bytes */afs_uint32 st_blksize; /* blocksize for filesystem I/O */afs_uint32 st_blocks; /* number of blocks allocated */afs_uint32 st_atim; /* time of last access */afs_uint32 st_mtim; /* time of last modification */afs_uint32 st_ctim; /* time of last change */};
struct my_dirhandle{afs_uint32 type;afs_uint32 inode;char name[MYMAXPATH];};
typedef my_dirhandle bulkmydirhandles<>;/*********************phase 1 functions *********************************************/
rxc_getattr(IN string mypath<MYMAXPATH>, IN int dummy) split = 1;
rxc_getdirWrapper(IN string path<MYMAXPATH>, OUT bulkmydirhandles *handles) = 2;
rxc_read(IN string path<MYMAXPATH>;, IN afs_uint32 size, IN afs_uint32 offset,IN struct my_file_info *fi) split = 3;
rxc_open(IN string path<MYMAXPATH>, IN int flags, OUT u_int *hd) = 4;
rxc_write(IN string path<MYMAXPATH>,IN afs_uint32 size, IN afs_uint32 offset,IN struct my_file_info *fi) split = 5;
rxc_chmod(IN string path<MYMAXPATH>, IN afs_uint32 mode) = 6;
rxc_chown(IN string path<MYMAXPATH>, IN afs_uint32 uid, IN afs_uint32 gid) = 7;
rxc_utime(IN string path<MYMAXPATH>, IN afs_uint32 at,IN afs_uint32 mt) = 8;
rxc_mknod(IN string path<MYMAXPATH>, afs_uint32 mode, afs_uint32 rdev) = 9 ;
rxc_mkdir(IN string path<MYMAXPATH>, IN afs_uint32 mode) = 10;
rxc_unlink(IN string path<MYMAXPATH>) = 11 ;
rxc_rmdir(IN string path<MYMAXPATH>) = 12;
rxc_rename(IN string from<MYMAXPATH>, IN string to<MYMAXPATH>) = 13;
rxc_truncate(IN string path<MYMAXPATH>, IN afs_uint32 size) = 14;
rxc_release(IN string path<MYMAXPATH>, IN struct my_file_info *fi) = 15;
rxc_readlink(IN string path<MYMAXPATH>, IN afs_uint32 size,OUT stringdata<MYMAXPATH>) = 16;
rxc_symlink(IN string from<MYMAXPATH>, IN string to<MYMAXPATH>) = 17;
rxc_link(IN string from<MYMAXPATH>, IN string to<MYMAXPATH>) = 18;
rxc_statfs(IN string path<MYMAXPATH>, OUT struct mystatfs *stbuf) = 19;
rxc_fsync(IN string path <MYMAXPAT>, IN int isdatasync, IN struct my_file_info*fi) = 20 ;
rxc_flush(IN string path <MYMAXPATH>, IN struct my_file_info *fi) = 21 ;

在定义 RX RPC 层时,注意以下几点:

  • 在 statfsstat 和 fuse_file_info 基础上定义了 mystatfsmystat 和 my_file_info 的封装程序。它们都会使用所生成的 XDR 代码进行转换。(XDR(External Data Representation,外部数据表示)允许采用一种与体系结构无关的方式进行封装,这样就可以在异构计算机系统之间传输数据了。)
  • 您几乎要为 fuse_operations 结构的每个成员都定义一个函数,它们几乎具有相同的参数,因为 afsfuse_client 的工作就是负责接管 FUSE 文件系统中的调用,并将这些调用传递给 afsfuse_server。
  • 您已经硬编码了一些值,例如 MYMAXPATH,这应该从系统中获得 —— 硬编码是为了简单性起见而这样做的。

回页首

创建客户机和存根文件

接下来使用 rxgen 编译 afsfuse.xg 文件,从而创建客户机和存根文件。从包含 afsfuse_server 和 afsfuse_client 的源代码的目录中,运行命令openafs-1.2.13/i386_linux24/dest/bin/rxgen afsfuse.xg。这会创建以下文件:

  • afsfuse.cs.c 是与 afsfuse_client.c 进行链接的客户机存根代码。
  • afsfuse.h 是包含您的 FUSE RX 代码的各种定义的头文件。
  • afsfuse.ss.c 是与 afsfuse_server 代码进行链接的服务器存根代码(代理代码)。
  • afsfuse.xdr.c 包含了用来处理在 afsfuse.xg 中定义的 3 个结构所使用的代码。

现在为 afsfuse_client.c 和 afsfuse_server.c 添加一些执行实际工作的代码。大部分调用都如下所示:

  • Our_call_in_afs_fuse_client()。分析参数并准备执行 RPC。对 RX [RPC] 调用 afsfuse_server。组合参数。将这些值拷贝到传递给这个函数的行参数中。
  • Our_call_in_afs_fuse_server()。组合参数。调用本地文件系统或 AFS 特有的函数。分析参数准备执行 RPC。生成 RX RPC 调用。

afsfuse_client.c 调用如下所示:

int afsfuse_readlink(const char *path, char *buf, size_t size){rx_connection *local& int ret& char *buffer = malloc (512)&memset(buffer,0,512)& memset(buf,0,size)& local = getconnection()&ret = rxc_rxc_readlink(local,path,512,&buffer) // rpc callrelconnection(local)&strncpy(buf,buffer,512-1)&//<- demarshall the parametrsreturn ret&}

afsfuse_server.c 调用如下所示:

清单 3. afsfuse_server.c 调用
int rxc_rxc_readlink( struct rx_call *call, char * path, afs_uint32 size, char**data){ int ret& char lbuff[512] ={0}&translatePath(path,lbuff)& //<- make filesystem call*data = malloc(512)&res = readlink(lbuff, *data, 512-1)&if(res == -1) return -errno& (*data)[res] = '\0'& return 0&}

简单地,您可以在其他函数中添加代码来对文件系统进行增强。

您需要创建一个 makefile 来编译代码。记住在编译 afsfuse_client 的代码时包括以下选项:-D_FILE_OFFSET_BITS=64 和 -DFUSE_USE_VERSION=22

清单 4. 生成编译客户机代码使用的 makefile
SRCDIR=./ LIBRX=${SRCDIR}lib/librx.a
LIBS=${LIBRX} ${SRCDIR}lib/liblwp.a
#CC = g++
CFLAGS=-g -I. -I${SRCDIR}include -I${SRCDIR}include/fuse/ -DDEBUG ${XCFLAGS}-D_FILE_OFFSET_BITS=64 -DFUSE_USE_VERSION=22
afsfuse_client: afsfuse_client.o afsfuse.xdr.o ${LIBS} bulk_io.o afsfuse.cs.o${CC} ${CFLAGS} -o afsfuse_client afsfuse_client.o ${SRCDIR}lib/fuse/fuse.o${SRCDIR}lib/fuse/mount.o ${SRCDIR}lib/fuse/helper.o${SRCDIR}lib/fuse/fuse_mt.o bulk_io.o afsfuse.cs.o afsfuse.xdr.o ${LIBS}
afsfuse_server: afsfuse_server.o afsfuse.xdr.o afsfuse.ss.o bulk_io.o ${LIBS}${CC} ${CFLAGS} -o afsfuse_server afsfuse_server.o bulk_io.o afsfuse.ss.oafsfuse.xdr.o ${LIBS}
#afsfuse_client.o: afsfuse.h
#afsfuse_server.o: afsfuse.h
bulk_io.o: ${CC} -c -g -I${SRCDIR}include bulk_io.c afsfuse.cs.c afsfuse.ss.cafsfuse.er.c afsfuse.h afsfuse.xdr.c: afsfuse.xg rxgen afsfuse.xg
afsfuse.xdr.o: afsfuse.xdr.c ${CC} -c -g -I{SRCDIR}include afsfuse.xdr.c
all: afsfuse_server afsfuse_client
clean: rm *.o rm afsfuse_client rm afsfuse_server

记住,您仍然需要使用 librx.a 和 liblwp.a 链接到 RX 和 RX 使用的 LWP 代码上。fuse/fuse.o、fuse/helper.o 和 fuse/mount.o 都是代码需要链接的 FUSE 库。

回页首

结束语

在本文中,您已经学习了如何安装 FUSE 和 OpenAFS,以及如何使用它们来创建并定制自己的用户空间文件系统,它可以成为 Linux 中一个功能完备、稳定可靠的文件系统,这不需要对现有内核打任何补丁或重新编译 —— 您甚至都不需要成为内核模块程序员。您已经详细了解了启用 FUSE 文件系统的两个关键概念:如何安装和配置 FUSE 内核模块,以及如何充分利用 FUSE 库和 API 的功能。

使用 FUSE 开发自己的文件系统相关推荐

  1. Linux FUSE开发

    作者 QQ群:852283276 微信:arm80x86 微信公众号:青儿创客基地 B站:主页 https://space.bilibili.com/208826118 参考 github libfu ...

  2. 嵌入式环境:挂载开发板根NFS文件系统失败

    挂载开发板根NFS文件系统的时候,发现了下面的问题: Looking up port of RPC 100003/2 on 192.168.2.109 rpcbind: server 192.168. ...

  3. 【Linux系统开发】x210开发板根目录文件系统构建

    文章目录 一.开发板配置 二.了解rootfs 三.虚拟机文件配置 1.目录配置 2.x210_bsp配置 3.rootfs配置 4.make menuconfig 四.busybox的移植实战 1. ...

  4. Linux系统获取开发板的文件系统并打包成img文件

    应用情形: 在实际的开发中,由于原系统包含的功能有限,而根据项目的需要,安装了相应的库及运行项目程序所创建的各种文件,和所做     的各种配置,想将调试好的系统打包发布,进行批量生产,就可参考本文提 ...

  5. linux fuse安装脚本,Linux FUSE(用户态文件系统)的使用:用libfuse创建FUSE文件系统...

    说明 FUSE 是Linux Kernel的特性之一:一个用户态文件系统框架,a userspace filesystem framework. 形象的说就是可以在用户态运行一个程序,这个程序暴露出一 ...

  6. DM368开发 -- 制作ubifs文件系统

    一.Ubifs 文件系统的制作和启动 (重点)参看:UBIFS Support 参看:UBIFS - UBI File-System UBI 文件系统是 JFFS2 的下一代文件系统,更适合 NAND ...

  7. 迅为IMX6开发板Buildroot文件系统4G拨号上网工具测试

    给IMX6开发板烧写好 buildroot 的文件系统,连接好 4G 模块和天线并将 SIM 卡插入卡槽启动如下图: 输入用户名 root ,密码 :XXXXXX 开始测试 ppp 拨号,先将 eth ...

  8. 十九.Linux开发之根文件系统移植——根文件系统的原理

    有道云笔记地址: 详情看这里链接,记录太多,就不一一排版了. http://note.youdao.com/noteshare?id=f9c7c1b589233d7b6ed661c3749f1ce8& ...

  9. Linux下的C编程实战(开发平台搭建,文件系统编程,进程控制与进程通信编程,“线程”控制与“线程”通信编程,驱动程序设计,专家问答)

    Linux下的C编程实战(一) ――开发平台搭建 1.引言 Linux操作系统在服务器领域的应用和普及已经有较长的历史,这源于它的开源特点以及其超越Windows的安全性和稳定性.而近年来,Linux ...

最新文章

  1. python周末列表的表示形式合并_Python 列表合并题
  2. Jenkins 流水线 获取git 分支列表_jenkins的安装和配置 自动化部署 码云 gitee
  3. PowerDesigner 使用的一些技巧(转)
  4. 优化数据库大幅度提高Oracle的性能
  5. python之函数用法islower()
  6. php冒泡排序和快速排序笔记
  7. Spring3向Spring4升级过程中quartz修改
  8. 在任何无法理解的情况下,请编写脚本
  9. 【CodeForces - 1051B】Relatively Prime Pairs (构造,思维,素数,水题)
  10. 产品经理,如何降噪学习?
  11. Fixjs实践——标签、按钮控件
  12. Firefox历史版本下载
  13. 设计模式总结之模式分类
  14. origin中文版散点图拟合曲线_Origin使用自定义函数拟合曲线函数
  15. 在JavaScript中实现继承的几种方式
  16. CSDN:2020 年度 CSDN 博客之星评选——28 号【沉默王二】,感谢你投上的宝贵一票,感谢!
  17. c语言2维数组每一行最小值,二维数组每一行最大值
  18. 【黄啊码】PHP配合微信公众号生成推广二维码
  19. 计算机应用基础实验报告心得体会,计算机应用基础实训总结报告
  20. BUCK降压斩波电路MATLAB SIMULINK仿真

热门文章

  1. Web项目中使用Spring 3.x + Quartz 2.x实现作业调度详解
  2. 【转】64位ORACLE客户端上plsql无法识别ORACLE_HOME解决方案
  3. openstack中文文档
  4. 养肝粥,用电脑过度人群必备! - 健康程序员,至尚生活!
  5. 【代码保留】WebService发布本地磁盘信息
  6. 【机器人】9-10月项目疑惑
  7. 安卓端和IOS端小程序蓝牙通讯问题
  8. k8s部署zabbix_Kubernetes 中部署 Zabbix
  9. 海外php程序员,国外PHP程序员的13个好习惯_php
  10. java web filter 入口_springboot 通过@WebFilter(urlPatterns )配置Filter过滤路径