GlusterFS Translator API



简介

在深入translator api前,我们必须理解关于此api所在上下文的两个概念。

一个是文件系统api,主要通过分配表xlator_fops,来展露大多数文件系统功能。xlator_fops,就是组合linux vfs的file_operations, inode_operations, and super_operations三类操作在一个数组。要理解xlator如何工作,必须理解这些调用做什么,相互关系是什么,比如open/lookup/close,read/write,opendir/readdir,truncate,symlink等。

一个是xlator api的基础:是异步的并是基于回调的。意思是处理某个请求的代码必须分为两部分,一部分是在进入下一个xlator前的处理,一部分是待下一个xlator处理完成请求后。用另一种方式解说,就是你的分配函数(xlator_fops的前半部分)调用下一个xlator的分配函数,然后直接返回,并不阻塞。你的回调函数(后半部分)也许当你调用下一个xlator的分配函数时就很快被调用,或者后面某个时间从某个完全不同的线程中被调用(常常是网络传输的poll线程)。在两种情况下,回调都不能仅仅是从栈中取得上下文。Glusterfs确实提供了多种方式来在分配函数和回调函数间保持和传递上下文,但是不能仅仅依靠栈,需要我们编码处理一些事情。

分配表和默认函数

一个xlator的主要的分配表常常是fops(xlator动态库加载代码利用dlsym查找操作op名)。fops包含指向所有正常文件系统的操作的指针。仅仅需要填充在本xlator我们感兴趣的操作。任何其他的操作在运行时基于默认值来填充,并确定个回调函数,并直接把此请求发到下一个xlator。

默认函数和回调函数除了提供默认功能,也有其他目的。当我们需要添加某个新函数时到一个xlator时,我们可以拷贝和修改那些相同操作的默认函数等。这保证了正确的参数列表和合理的默认行为。每个xlator也许有另外的分配表,比如cbk表,此表用来管理inode和文件描述子的生命周期(看inode和file descriptor context部分)

STACK_WIND 和STACK_UNWIND宏

STACK_WIND and STACK_UNWIND是基于回调机制来实现的。这两个函数不作用于gdb里类似的调用栈,而是一个独立维护的栈,每个栈帧表示对xlators的调用。当某个fop从内核传递过来要调用时,这个调用就作为一个request请求,从FUSE xlator 经过DHT/AFR等到client xlator 直到server xlator 最后到posix xlator。 我们可以在用户空间fop入口点做任何需要的处理(这里应该就是fuse xlator),然后使用STACK_WIND沿着volume 文件设置的xlator 路径,传递此请求。

define STACK_WIND(frame, rfn, obj, fn, params …)

STACK_WIND (frame, default_setxattr_cbk, FIRST_CHILD(this), FIRST_CHILD(this)→fops→setxattr, loc, dict, flags, xdata);

此函数的参数解释如下:
  • frame:表示此请求的栈帧 stack frame。

  • rfn:当下一个xlator或者其他xlator完成了此请求(包括前半部分和后半部分)后,要调用的回调函数。

  • obj:要传递到的那个xlator对象,也即下一个xlator,不是调用STACK_WIND的本xlator。多个子的情况如何处理?

  • fn:要调用的具体的xlator的操作,来自下一个xlator的fops分配表。

  • params …任何其他的入口点相关的参数(比如针对此fop的inodes, file descriptors, offsets, data buffers等)如前叙,rfn回调函数也许在STACK_WIND的调用内部被调用,也或者后来在另一个环境中被调用。不调用下一个xlator就完成请求比如从cache中读数据,或者当本xlator层完成对req的处理,要从本层的回调中把请求传回上一个xlator时,我们使用STACK_UNWIND。实际上,我们最好使用 STACK_UNWIND_STRICT。这个函数允许我们确定请求的种类,比如是open还是close等。

#define STACK_UNWIND_STRICT(op, frame, params …)

STACK_UNWIND_STRICT (symlink, frame, op_ret, op_errno, inode, buf, preparent, postparent, xdata);

参数解释
  • 参数op:操作类型,是open/还是其他。用来检查额外参数是否匹配此fop。

  • params …:此请求的一些额外的参数

  • 另外,在op和params直接,还有两个参数op_ret和op_errno.虽然没有在宏定义中出现。但在调用时还是要有的。

    • op_ret:fop的到目前的状态(读的字节数或者写的字节数,常常0表示fop成功,-1表示失败)

    • op_errno:标准错误码,在fop失败的情况下

  • 具体fop相关的参数有其他特殊的参数,我们可以在params …中实现。但是我们也经常需要这两个参数

    • frame:当前请求的stack frame。

    • this:表示此xlator实例的xlator对象

回调函数跟分配函数类似,除了在这两个参数间有一个额外的参数cookie。是一个未定义数据类型的指针,由相应的STACK_WIND存储。默认情况下,这是一个由STACK_WIND生成的stack frame的指针,但是STACK_WIND_COOKIE可以允许我们确定一个不同的值。在这种情况下,这个处于rfn和obj直接的额外参数,可以从分配函数到其回调函数传递某些上下文。注意这个指针不能指向栈上的任何内容,因为当回调函数调用时,stack可能已经不存在了。

另一个需要注意的:STACK_UNWIND也许会导致整个调用栈退出,此时,最后的那个STACK_UNWIND调用将会释放所有的frames。因此,永远不要期望在调用STACK_UNWIND后,当前的frame内容还是完好的。

Per Request Context请求上下文私有变量

每个xlator 栈帧有一个local 指针,是用来保存xlator相关的上下文。这是最基本的机制,用来在分配函数和回调间保存上下文。因此我们应该习惯如下代码模式

/* in dispatch function */

local = (my_locals_t *)GF_CALLOC(1,sizeof(*local),...);
if (!local) {
/* STACK_UNWIND with ENOMEM eror */
}
/* fill in my_locals_t fields */
frame->local = local;
/* in callback */
local = frame->local;

要记住:每个帧frame的非NULL的local域当要毁栈时要用GF_FREE来释放,不用做其他的清理工作。如果local结构里包含指针或对其他对象的引用,需要我们自己进行这些资源的清理。在毁栈前,内存或者其他资源先清理是个好的习惯,为此就不能依靠GF_FREE来自动清理。最安全的方式是定义我们自己的xlator相关的destructor,在调用STACK_UNWIND前手动调用。

Inode and File Descriptor Context

大多数分配函数和回调以文件描述子或节点inode为参数。file descriptor (fd_t) or an inode (inode_t)。常常,xlator需要保存一些这些对象的一些关于此xlator的一些上下文,以此,在一个请求的完整生命周期内信息可以保持。例如,DHT xlator需要保存目录的layout map和某些inode的已知的位置。有一套函数来存储操作此类上下文。在每个函数中,第二个参数是此value相关的xlator对象的指针。value是64位无符号整形。

inode_ctx_put (inode, xlator, value) /* NB: put, not set */把value放到inode里?

inode_ctx_get (inode, xlator, &value)从inode中获取值到value。

inode_ctx_del (inode, xlator, &value)

fd_ctx_set (fd, xlator, value) /* NB: set, not put */

fd_ctx_get (fd, xlator, &value)

fd_ctx_del (fd, xlator, &value)

_del函数实际是破坏性get,先返回然后删除值。inode函数有两个value的形式,比如inode_ctx_put2,操作两个值。使用xlator指针作为key/index并不仅仅是为了好看。当要删除inode_t or fd_t时, 删除代码要浏览上下文槽(估计就是遍历所有xlator)。对于每个使用inode_t or fd_t的xlator的上下文槽,查看xlator的cbk表,调用其forget 或者release。如果上下文是个指针,需要手动释放资源。传递到分配函数和回调的inode_t or fd_t pointer参数,仅仅是个 borrowed reference。如果需要保证以后对象还在,需要调用inode_ref or fd_ref,来达到permanent reference。当不再需要引用时可以 inode_unref or fd_unref。

Inode and File Descriptor Context 词典和xlator选项

另一个常见的参数类型dict _t,是一种通用词典或者hash-map的数据结构,用来以字符串key/索引的方式保存任意值。例如,值可以是可变大小的有无符号的整形,字符串,二进制blob。字符串,二进制blob也许可以用GLusterfs函数free,也可以用glibc free。或者不用free。保存引用计数的值的dict_t* 和the *data_t 对象,在引用数为0的情况下释放资源。和inode和文件描述子一样,如果想在以后使用接受到dict_t的对象,需要使用add _ref and _unref来管理其生命周期。词典不仅仅用于分配函数和回调函数。也用于传递options到模块,比如xlator的init函数所用的options。事实上,目前的xlator的init函数主体大部分是来解释包含在词典里的options。要给xlator添加一个option,也需要向xlator的options 数组里添加一项。每个option可以是个boolean/整型/字符串/路径/xlator 名字/其他类型。如果是个字符串,可以确定一个正确的值的列表。解析后的options,加上其他xlator级别的信息,将存储在xlator_t structure结构(在大多数上下中用this表示)的private的成员变量中。

Child Enumeration and Fan Out 子xlator枚举和扇出

在一个xlator里的代码,经常需要枚举其子xlator。要不找到一个满足要求的子xlator的或者操作所有的子xlator。例如,对于DHT xlator,需要从所有的子xlator收集 hash-layout 映射,以确定文件应该放到哪个子xlator;对于 AFR xlator,需要从子xlator中提取同一文件将要执行的操作个数以便确定replication复制状态。代码范例如下: xlator_list_t *trav; xlator_t *xl;

for (trav = this->children; trav; trav = trav->next) {
    xl = trav->xlator;
    do_something(xl);
}

如果目的是扇出一个请求到所有的子xlator,则需要一些努力。在一个分配函数里最常用的方式是如下 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);
/* Do whatever you do for every call */
if (!call_cnt) {
    /* Do last-call processing. */
    STACK_UNWIND(frame,op_ret,op_errno,...);
}
    return 0;
在某些情况下,可以使用STACK_WIND_COOKIE,这样可以在回调中知道具体哪个调用返回了。可以查看AFR。

Call Frames and Call Stacks

所有的xlator 函数使用类似SEDA和AT&T的STREAMS的异步调用规范。需要的调用帧和调用栈数据结构功能上几乎与Windows NT里的I/O requst packets 和irp 栈相同。许多xlator函数类似与fuse对应的函数,但是修改了或者添加了其他参数。

在xlator api里这是最基本的数据结构,call_frame_t and call_stack_txlators环境可以看作是个线程库,每个请求有自己一个轻量级线程。这个线程有自己的栈,但是这些调用栈比c栈更加结构化,同时常常偏离栈的严格的FIFO语义。每个调用帧表示一个函数调用,函数调用也许是嵌套的。但是这些函数在c里实际上两个调用--初始部分执行并在进行任何嵌套调用前返回。收尾部分在嵌套的调用完成后执行。

call_frame_t的成员如下:
  • root:指向内嵌在call_stack_t里的假帧,不像其他单独分配的frame。在stack里对所有的frame都相同。

  • parent指向调用此frame的那个frame。用c说就是调用函数 calling 函数。

  • next /prev指向下一个/前一个要完成的frame。注意不是要调用的那个帧,

  • local 私有数据,针对此帧调用的私有数据。是xlagor函数的本地变量,保存在此,可以在分配函数和回调间保持。

  • this 指向xlator的私有全局状态。

  • ret 指向调用者(上层xlator)的回调函数,当本xlator的req完成后执行。

  • ref_count and complete 调度器用来跟踪那个frame是active,哪个完成了,哪个需要恢复执行。

  • lock and cookie:

正常情况下,新frame push到栈后,next就跟parent就相同了。但是,一个frame(帧)可以分支。例如,stripe_writev请求的帧,在每个子xlator返回前生成一个新帧。假设有3个子xlator,即3个子卷。next就只与第一个新帧相同。

根据线程库模型,call_stack_t对应的就是线程控制块。

有用的成员变量有:
  • uid 和gid表示调用帧来自谁,用来鉴权和认证。

  • pid表示发起此请求的进程pid。

  • trans 指向请求相关的transport 结构。在服务器端用来实现基于节点的存取控制,或者用来沿着失败的连接跟踪请求。在client用作啥用?

  • frames:假帧,指向请求的第一个真正的frame。

  • op /type?

  • call_pool_t?

STACK_WIND and STACK_UNWIND等价与函数的调用和返回。在本xlator的请求的执行,分配表先执行,wind后就停止只ing了。当函数rfn回调函数执行时,此操作就开始恢复执行了。rfn也许被调用多次。(多个子卷)。

todo
  • call stack

  • call stub


展开阅读全文

Git 实用技巧

11-24
这几年越来越多的开发团队使用了Git,掌握Git的使用已经越来越重要,已经是一个开发者必备的一项技能;但很多人在刚开始学习Git的时候会遇到很多疑问,比如之前使用过SVN的开发者想不通Git提交代码为什么需要先commit然后再去push,而不是一条命令一次性搞定; 更多的开发者对Git已经入门,不过在遇到一些代码冲突、需要恢复Git代码时候就不知所措,这个时候哪些对 Git掌握得比较好的少数人,就像团队中的神一样,在队友遇到 Git 相关的问题的时候用各种流利的操作来帮助队友于水火。 我去年刚加入新团队,发现一些同事对Git的常规操作没太大问题,但对Git的理解还是比较生疏,比如说分支和分支之间的关联关系、合并代码时候的冲突解决、提交代码前未拉取新代码导致冲突问题的处理等,我在协助处理这些问题的时候也记录各种问题的解决办法,希望整理后通过教程帮助到更多对Git操作进阶的开发者。 本期教程学习方法分为“掌握基础——稳步进阶——熟悉协作”三个层次。从掌握基础的 Git的推送和拉取开始,以案例进行演示,分析每一个步骤的操作方式和原理,从理解Git 工具的操作到学会代码存储结构、演示不同场景下Git遇到问题的不同处理方案。循序渐进让同学们掌握Git工具在团队协作中的整体协作流程。 在教程中会通过大量案例进行分析,案例会模拟在工作中遇到的问题,从最基础的代码提交和拉取、代码冲突解决、代码仓库的数据维护、Git服务端搭建等。为了让同学们容易理解,对Git简单易懂,文章中详细记录了详细的操作步骤,提供大量演示截图和解析。在教程的最后部分,会从提升团队整体效率的角度对Git工具进行讲解,包括规范操作、Gitlab的搭建、钩子事件的应用等。 为了让同学们可以利用碎片化时间来灵活学习,在教程文章中大程度降低了上下文的依赖,让大家可以在工作之余进行学习与实战,并同时掌握里面涉及的Git不常见操作的相关知识,理解Git工具在工作遇到的问题解决思路和方法,相信一定会对大家的前端技能进阶大有帮助。
©️2020 CSDN 皮肤主题: 大白 设计师: CSDN官方博客 返回首页
实付0元
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值