PVFS2 源代码分析之用户系统接口src/client/sysint/client-state-machine状态机生命周期和上下文

来源:互联网 发布:java注解有什么用 编辑:程序博客网 时间:2024/05/18 18:20

通过分析状态机的执行逻辑,状态机状态转移和触发嵌套/子状态机的执行过程已经基本清楚,但是一个状态机从创建、启动,到挂起、终止的完整生命周期还需进一步分析。本文涉及多个文件,但主要接口函数包含在src/client/sysint目录下的client-state-machine.h和client-state-machine.c文件中,这些接口函数会调用src/common/id-generator和src/io/job目录下的相关函数。

说明:如下函数名后的括号注明其所在文件,贴出代码为除去gossip日志和次要无关逻辑之后的核心代码,“// ......”表示此处有省略。另外,背景色相同的行为是配对的,显式标出以方便理解前后对应的流程。 

  • 创建

我们知道,状态机的标识是状态机控制块smcb,分配smcb意味着状态机的创建,见函数1。

函数1. PINT_smcb_alloc(src/common/misc/state-machine-fns.c)

该函数用于分配状态机控制块。第3行参数op是状态机代表的系统操作;第5行参数方程用于将op映射为对应状态机的地址,并依据找到的状态机定义设定smcb->current_state,为执行动作函数和状态转移做好准备;第6行参数是回调函数,在状态机终止时调用;第7行参数是该应用程序的上下文(context),以区别于同时进行的其他应用程序(应用程序指src/apps下的各个程序),详见最后一节“上下文”。

 

为了更好地说明参数含义,我们以分析过的用户系统接口sys-get-eattr为例,见函数2。

函数2. PVFS_isys_geteattr_list(src/client/sysint/sys-get-eattr.c)

该函数最终被状态机对外接口函数PVFS_sys_geteattr调用。它调用PINT_smcb_alloc时,状态机结束的回调函数设为client_state_machine_terminate,参见函数10。

 

第10行参数op赋值为PVFS_SYS_GETEATTR,其定义来自src/client/sysint/client-state-machine.h:

op参数赋值给smcb的成员op,用于表达状态机进行的操作,实际上记录了使用哪个状态机。

【注意】另一个参数op_id与之形似但无关,它是在状态机挂起时,在哈希表中注册对应smcb时获得的索引,可视作状态机的一个句柄。

    • 启动

    在状态机的执行逻辑中,我们分析了PINT_state_machine_start函数,而这里主要讨论调用该函数的环节和相关操作。

    由上节函数2的第20~21行可知,启动状态机是通过调用PINT_client_state_machine_post函数,即下述函数3。它在第15行调用了PINT_state_machine_start函数。

    函数3. PINT_client_state_machine_post(src/client/sysint/client-state-machine.c)

    除了调用PINT_state_machine_start函数外,它所做的后续工作包括:如果第17行PINT_smcb_complete函数显示状态机已经立即结束了,op_id赋值为-1,否则需将挂起的状态机进行注册(第28行),注册时获得该状态机的句柄(存储于sm_p->sys_op_id,见第31行),供以后的操作定位状态机。注册函数PINT_id_gen_safe_register只是包裹了src/common/id-generator/id-generator.c中的id_gen_safe_register函数,见函数4.

    函数4. id_gen_safe_register(src/common/id-generator/id-generator.c)

    第14行,smcb注册后获得了其在quickhash类型的全局变量s_id_gen_safe_table中的唯一句柄并赋值给new_id传递出去。通过该句柄可获得其对应的状态机控制块。

      • 挂起

      状态机执行返回SM_ACTION_DEFERRED,表明当前状态机在等待子状态机或其他耗时操作,我们将此状态描述为挂起状态。

       

      还是以用户系统接口sys-get-eattr为例。上面“创建”一节举出的PVFS_isys_geteattr_list函数是非阻塞的,函数返回并不意味着状态机运行结束。该函数被其阻塞式同名函数(参数不同)调用,见函数5.

      函数5. PVFS_sys_geteattr_list(src/client/sysint/sys-get-eattr.c)

      在第12~13行,这个阻塞的PVFS_isys_geteattr_list通过调用非阻塞同名函数获得其op_id。然后在第21行调用PVFS_sys_wait函数等待状态机结束(等待时即阻塞状态)。而PVFS_sys_wait只是包裹了PINT_client_wait_internal函数,如下。

       

      【注意】第24行,状态机完全结束后,调用PINT_sys_release函数取消注册op_id,并释放对应状态机控制块smcb。

      函数6. PINT_client_wait_internal(src/client/sysint/client-state-machine.c)

      在第12行,先由句柄op_id获得对应的状态机控制块;然后在第16行,该函数不断调用PINT_client_state_machine_test函数检查操作是否结束。PINT_client_state_machine_test函数较为关键,见函数7.

      函数7. PINT_client_state_machine_test(src/client/sysint/client-state-machine.c)

      第17~24行检查当前状态机是否结束,如果结束就直接进行清理工作,调用conditional_remove_sm_if_in_completion_list函数(第21行)将对应smcb从全局变量s_completion_list中删除,该变量收集所有挂起的状态机控制块(【注意】不只限于某个上下文context)。其声明如下:

      第25~30行,通过函数job_testcontext检查当前上下文中挂起但可继续的任务(job),调用后参数smcb_p_array指向可继续的状态机控制块的数组,数组大小存在参数job_count中。而后在第39行逐个激活这些状态机,使之继续执行。

       

      job_testcontext主要是包装了对completion_query_context函数的调用,额外功能是设置了一个等待时间上限(如上第29行参数“10”表示超时时限为10秒)。其核心调用completion_query_context函数如下。

      函数8. completion_query_contex(src/io/job/job.c)

      其中最关键的数据结构是completion_queue_array,一个由quicklist构成的数组,其声明如下:

      该数组的索引即为上下文ID(参见后面“上下文”一节),每个数组元素是quicklist类型的链表,构成完成队列(completion queue),队列由一些挂起但已经可以继续执行的状态机组成。下节函数9提供了completion_queue_array的来源之一,涉及典型的可继续状态机。

        • 终止

        状态机终止时除了清理工作,还包括向completion_queue_array中添加元素。

        函数9. PINT_state_machine_terminate(src/common/misc/state-machine-fns.c)

        当状态机返回SM_ACTION_TERMINATE的时候,调用该函数。如果当前状态机有父状态机(第7行)且父状态机不再有子状态机(第22行),那么就将父状态机控制块作为一个空任务添加到completion_queue_array(第27行job_null函数所做的工作),表明该父状态机可以继续了,这通常发生在最后一个子状态机运行完毕之时。最终,父状态机控制块会被job_testcontext取出来继续执行。

         

        第34行调用了状态机结束时的回调函数,该函数在分配状态机控制块smcb时通过PINT_smcb_alloc设定(参见函数1)。该回调函数通常设定为client_state_machine_terminate,下面我们关注一下该回调函数所做的清理工作。

        函数10. client_state_machine_terminate(src/client/sysint/client-state-machine.c)

        除去取消的IO操作和直接完成的状态机(第8~11行),都会将smcb添加到全局变量s_completion_list中(第14行add_sm_to_completion_list函数完成此工作),直到彻底完成时再删除(参见函数7)。

            • 上下文

            上面有几处提到状态机的上下文context,也是很多函数的参数。上下文一一对应于应用程序,即每个进程开辟一个独有的上下文。因为C语言没有可以保存状态的对象,所以需要在函数调用中层层传递,以保证可以分别系统操作(状态机)的归属。

             

            从实现角度说,任何src/apps/admin文件夹下的应用程序都会调用src/common/misc/pvfs2-util.c文件中的PVFS_util_init_defaults函数,而该函数调用了src/client/sysint/initialize.c中的PVFS_sys_initialize函数,进而调用src/client/sysint/client-state-machine.c中的PINT_client_state_machine_initialize函数。这个函数中就调用了函数11来创建context:

            函数11. job_open_context(src/io/job/job.c)

            该函数通过参数context_id返回新开辟的上下文ID。第6~12行在上下文数目上限内,找到可以存放完成队列的数组位置;第15行在该位置创建新的属于该上下文的完成队列。

             

            原创粉丝点击