zoukankan      html  css  js  c++  java
  • 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内的代码。

  • 相关阅读:
    hdu6229 Wandering Robots 2017沈阳区域赛M题 思维加map
    hdu6223 Infinite Fraction Path 2017沈阳区域赛G题 bfs加剪枝(好题)
    hdu6438 Buy and Resell 买卖物品 ccpc网络赛 贪心
    hdu6441 Find Integer 求勾股数 费马大定理
    bzoj 1176 Mokia
    luogu 3415 祭坛
    bzoj 1010 玩具装箱
    bzoj 3312 No Change
    luogu 3383【模板】线性筛素数
    bzoj 1067 降雨量
  • 原文地址:https://www.cnblogs.com/mingziday/p/2323098.html
Copyright © 2011-2022 走看看