• GlusterFS Translator API介绍


    英文原文:http://hekafs.org/dist/xlator_api_2.html
    中文转载来源:http://blog.sina.com.cn/s/blog_7765b3c60100z7lh.html
    需要修改和完善 

    介绍

    在我们开始讲解该API细节时,需要明确两个重要点。第一个是文件系统API,其重要功能通过一个dispatch table输出。在GlusterFS中是xlator_fops, 它是Linuxfile_operations, inode_operationssuper_operatioins的组合。为了理解translators如何工作,你必须首先了解这些函数的功能及其相关影响,比如open/close, read/write, opendir/readdir, truncate, symlink等。

    第二个是translator API采用异步和回调函数的机制。这意味着你处理特定请求的代码必须被分为2个部分,以便在下一个translator之前后和之后查看该请求。换句话说,你的dispatch(first-half)函数调用下一个translatordispatch 函数,然后无阻塞的返回。当你调用下一个translatordispatch函数时,你的回调函数(second-half 函数)可能会立即被调用,或者它可能稍后在一个不同的线程上被调用(通常是network tranportpolling 线程)。在两种情况下,回调函数都不会像同步函数那样获取其上下文。GlusterFS提供了几种方式用于在dispatch函数及其回调函数间保存和传递上下文,但是你必须自己处理某些事情而不能完全依赖协议栈。

    Dispatch table和默认函数

    Translator的主要dispatch table被称为fops( translator 调用dlsym通过名称加载具体代码)fops包含了所有的指向文件系统函数的指针。对于特定的translator只需指定相关的函数。其它的部分会在运行时自动填入默认的值,并直接传递给下一个translator, 同时指定回调函数,回调函数传回之前translator的结果。

    默认函数和回调函数除了实现默认的功能外,还有其它用途。任何时刻当你需要向translator添加函数,最简单的方法是先拷贝并修改对应的默认函数,比如default_open, default_truncate.这样能够保证函数参数列表的正确性和默认的行为。然后只需要更新fops指向最新的拷贝。

    当你拷贝并重命名一个默认函数时,你的拷贝函数通常使用默认的回调函数(比如,default_open会参考defalut_open_cbk)。通常这就是你所需要的;如果你在传递请求前就完成了所有的工作,你也许根本不需要回调函数,此时也可以使用默认的。如果不是以上的情况,拷贝并重命名默认的回调函数,如同修改dispatch函数,从而保证正确的参数列表。

    每一个translator包含附加的dispatch talbes,包括cbk表用于管理inodefile描述符生命周期。

    STACK_WINDSTACK_UNWIND

    Translator API的回调机制主要采用了STACK_WINDSTACK_UNWIND.这些操作并不是你通常在gdb看到的stack,这是一种专用的frame stack,用于表示对translators的调用。当你的fops某函数进入点被调用,该调用表示一个请求,从客户端的FUSE到服务器端的本地文件系统。你的进入点可以执行相应的操作,然后把该请求使用STACK_WIND传递给下一个translator.其参数如下:

    Frame: stack frame代表请求

    Rfn: 回调函数,当下一个translator完成时会调用该函数

    Obj: translatro对象,听从其控制

    Fn:从下一个translatorfops table中指定要调用的translatron函数

    Params..:任何其他被调用函数的参数(比如,inodes, file描述符,offset, data buffer

    正如之前章节提到的那样,你的”rfn”回调函数也许在调用STACK_WIND内被调用,也许稍后被不同的上下文调用。为了完成一个请求而不用调用下一个translator(比如从cache中返回数据),或者当任务完成从回调函数中回到上一个translator时调用STACK_UNWIND.实际上,你最好使用STACK_UNWIND_STRICT,它可以用来指定那类请求你已经完成了。其参数如下:

    Op:操作类型(比如open,rename,用来检查附加的参数符合函数的期望。

    Params..:附加的参数,依据请求的类型

    在实际中,基本上大部分的请求类型使用两个附加参数opparams,尽管它们不在宏定义中:

    Op_ret:目前为止操作的状态(有时是readwrite的字节数,更多的用0表示成功,用-1表示失败)

    Op_errno:一个标准的错误码(比如EPERM,如果函数执行失败。

    Dispatch及其回调函数的参数列表是和每个函数具体相关的,但是前几项通常是这些:

    Frame:当前请求对应的stack frame

    This: 一个translator对象,代表translator的实例

    回调函数也是类似的,除了在两者之间多了一个参数。该参数是cookie,这是一个opaque指针,有对应STACK_WIND存储。默认情况下是一个指向由STACK_WIND创建的strack frame的指针,同时还提供STACK_WIND_COOKIE允许指定不同的值。在这种情况下,rfnobj间附加的附加的参数因STACK_WIND各异,同时可以用来传递从dispatch到回调函数的一些上下文。注意,它不能是指向stack中任何结构的指针,因为当回调函数调用时该stack可能已经不存在了。

    另外要注意,STACK_UNWIND能引起整个调用stackunwound,在某一点最后的调用函数释放其所有的frames.出于这个原因,你不能在调用STACK_UNWIND后仍要求对该frame执行有影响的操作。

    调用上下文

    每个translator-stack frame都有一个local指针,用来储存该translatro特定的上下文。这是在dispatch和回调函数间存储上下文的主要机制,所以你应当熟悉以下的模式:

        local = (my_locals_t *)GF_CALLOC(1,sizeof(*local),...);

        if (!local) {  

        }

        frame->local = local;  

        local = frame->local;

    需要牢记的一点是:当该stack被销毁是,每个framelocal如果不为NULL都会被传递给GF_FREE,但是不会执行其他的清理工作。如果你的的local结构体包含指针或引用其他对象,那么你需要仔细处理这些。如果内存和其它资源能在stack被销毁前被释放是较理想的情况,所以不要完全依赖自动的GFS_GFREE.取而代之,最安全的做法是定义特定translator的销毁函数,并在STACK_UNWIND返回前手工调用,销毁函数形如:

        void my_destructor (call_frame_t *frame)

        {

            my_own_cleanup(frame->local);

            GF_FREE(frame->local);   

            frame->local = NULL;

        }

    如果call_frame_t包含一个指向销毁的函数的指针,并自动在STACK_UNWIND中调用,这种方式比较理想;并且如果local结构被更加高效的处理,而不是对每个translator需要两段旅程并通过glibc内存申请;但是现实情况却不是这样的。

    Inode和文件描述符上下文

    大部分的dispatch函数和回调函数以文件描述符(fd_t)inode(inode_t)作为参数。通常,你的translator需要存储一些自有的上下文,这些上下文独立与单个请求的生命周期。比如,DHT存储目录对应的布局映射(layout map),和某inode最后可知的位置。Glusterfs提供了一系列的函数用于存储此类上下文。在每种情况下,第二个参数是一个指向translator对象的指针,需要存储的数据与其相关,存储的值是一个unsigned64位整数。这些函数返回0代表成功,在_get_del函数中使用引用参数而不是返回值。

    inode_ctx_put (inode, xlator, value)

    inode_ctx_get (inode, xlator, &value)

    inode_ctx_del (inode, xlator, &value)

    fd_ctx_set (fd, xlator, value)

    fd_ctx_get (fd, xlator, &value)

    fd_ctx_del (fd, xlator, &value)

    _del函数是”destructive get”,返回并销毁存储的值。同时,inode对应的函数有2-值的格式,允许对每个translator操作两个值而不是一个。

    使用translator对象指针作为key/index并不是为了装饰。当一个inode_tfd_t被删除时,删除代码通过上下文查找一些内容。对每一个实体,它寻找translatorcbk dispatch表,并调用forgetinoderelease对文件描述符。如果该上下文是一个指针,那么就由你释放相关的资源。

    最后,请牢记传递给dispatch 函数和callbackinode_tfd_t指针只是borrowed的应用。如果你希望该对象稍后还存在,最好调用inode_reffd_ref增加一个持久引用,并且当引用不再需要时调用inode_unreffd_unref.

    字典和translatro选项

    另外一个常用的类型是dict_t,它是一个通用的排序字典或hash-map的数据结构,可用来存放任意的值并以字符串为键值。例如,存储的值可以是任意大小的有符号或无符号整数,字符串,或二进制。字符串和二进制需要被标记在不被需要时被glusterfs函数释放,由glibc释放或根本不释放。Dict_t**data_t对象都是引用计数的,只有当引用数为0时被释放。如同inodes和文件描述符,如果你希望通过参数接受的dict_t稍后仍存在,你必须调用_ref_unref处理器生命周期。

    字典并不是仅用于dispatch和回调函数。他们也被用于传递不同的模块选项,包括translatro初始化的选项。事实上,目前translatorinit函数主要用于解析字典中的选项。向translatro中添加一个选项,你需要在translatoroptions数组中添加一个实体。每一个选项可以是boolean,整数, 字符串,路径,translatro名称,和其它一些类型(见GF_OPTION_TYPE_.如果是字符串,你可以指定有效值。解析后的选项和其它的信息可以存放在xlator_t结构体中的private内。

    日志

    Translators中大部分的logging是通过gf_log函数。其参数包括字符串(通常是this->name),log等级,格式,根据格式的附加参数。日志等级分为GF_LOG_ERROR, GF_LOG_WARNING , GF_LOG_LOGGF_LOG_DEBUG.你可以封装gfs_log自定义相关宏,或采用官方的等级,这样你translator的日志在运行时就能被输出。在最简单的情形中,可以在gdb中进行调试。如果你更加有雄心壮志,你可以添加一个translator 日志等级选项。如果你有宏图伟志,你可以实现一个神奇的xattr调用用于传递新值。

    子元素遍历和fan out

    translator中的一个常用模式是便利子元素,可能是满足某项条件的子元素或者是所有的子元素。例如,DHT需要从所有子元素中收集基于hash-layout的地图,然后决定文件的去处;AFR需要从子元素取得同文件的pending的操作数用于决定副本状态。其格式如下:

        xlator_list_t *trav;

        xlator_t *xl;

        for (trav = this->children; trav; trav = trav->next) {

            xl = trav->xlator;

            do_something(xl);

        }

    如果目标是fan out一个请求到所有的子元素,需要一些其它的注意事项。通常采用的模式如下:

        local->call_count = priv->num_children;

         for (trav = this->children; trav; trav = trav->next) {

            xl = trav->xlator;

            STACK_WIND(frame,my_callback,xl,xl->fops->whatever,...);

        }

    然后,在回调函数中:

        LOCK(&frame->lock);

        call_cnt = --local->call_count;

        UNLOCK(&frame->lock);

        if (!call_cnt) { 

            STACK_UNWIND(frame,op_ret,op_errno,...);

        }

    在某些情况下,你可以使用STACK_WIND_COOKIE让每一个回调函数知道N个调用已经返回。具体的例子见AFR内的代码。

  • 相关阅读:
    bash组织成树数据结构
    statickeyword于C和C++用法
    POJ2239 Selecting Courses【二部图最大匹配】
    MVC过滤器的详细讲解和示范样本
    hdoj 2602 Bone Collector 【01背包】
    下的生产环境was重新启动不同意,怎么做?
    Qt Model/View 的简单说明
    View与Model绑定注意事项 (视图无数据显示)
    Qt Delgate的使用 简单说明
    QAbstractTableModel中的data()到底执行几遍???
  • 原文地址:https://www.cnblogs.com/mingziday/p/2323098.html
Copyright © 2020-2023  润新知