PVFS2源代码分析之输入输出src/io/job/job任务

任务(job)为相关组件和操作提供了一个异步执行的框架,该框架是有状态的(记录于任务描述符job descriptor ),可以记录异步调用执行到哪里等信息,并决定进一步操作;而下层函数调用不记录任务相关的状态(某些具体实现内部可能保存自身的状态),只通过函数参数输入输出任务状态信息。

 

job.c文件中定义的函数很多,因为要涵盖BMI、trove、flow、dev等多个组件的使用,但是不同组件具有相近的模式,我们可以将主要的函数分成如下几类(若干函数用前后缀表示,使用通配符“*”):

  1. 辅以时间管理器(time manager)的异步式
    job_bmi_send
    job_bmi_send_list
    job_bmi_recv
    job_bmi_recv_list
    job_flow
  2. 基于自定义队列的异步式
    job_req_sched_post
    job_req_sched_change_mode
    job_req_sched_post_timer
    job_req_sched_release
    job_trove_bstream_*
    job_trove_keyval_*
    job_trove_dspace_*
    job_trove_fs_*
  3. 同步式
    job_dev_write
    job_dev_write_list
  4. 基于线程管理器(thread manager)的异步式
    job_bmi_unexp
    job_dev_unexp

下面我们分别举例,分析各类函数源代码。同类中的各个函数非常接近,通常只是调用下层接口不同。

  • 辅以时间管理器的异步式

此类函数完成对下层函数的异步调用,将未立即完成的任务加入时间管理器 。我们以函数1为例,分析此类函数的主要行为,同类其他函数完成的主要步骤几乎相同,只是针对不同的下层函数。

 

函数1. job_bmi_send_list

int job_bmi_send_list( // ...... void *user_ptr, job_aint status_user_tag, job_status_s * out_status_p, job_id_t * id, job_context_id context_id, int timeout_sec, PVFS_hint hints) { /* post a bmi send. If it completes (or fails) immediately, then * return and fill in the status structure. If it needs to be tested * for completion later, then queue up a job_desc structure. */ int ret = -1; struct job_desc *jd = NULL; void* user_ptr_internal = NULL; /* create the job desc first, even though we may not use it. This * gives us somewhere to store the BMI id and user ptr */ jd = alloc_job_desc(JOB_BMI); // ...... jd->job_user_ptr = user_ptr; jd->u.bmi.actual_size = total_size; jd->context_id = context_id; jd->status_user_tag = status_user_tag; jd->bmi_callback.fn = bmi_thread_mgr_callback; jd->bmi_callback.data = (void*)jd; user_ptr_internal = &jd->bmi_callback; jd->hints = hints; /* post appropriate type of send */ if (!send_unexpected) { ret = BMI_post_send_list(/*......*/ user_ptr_internal, global_bmi_context, hints); } else { ret = BMI_post_sendunexpected_list(/*......*/ user_ptr_internal, global_bmi_context, hints); } if (ret < 0) { /* error posting */ out_status_p->error_code = ret; out_status_p->status_user_tag = status_user_tag; dealloc_job_desc(jd); jd = NULL; return (1); } if (ret == 1) { /* immediate completion */ out_status_p->error_code = 0; out_status_p->status_user_tag = status_user_tag; out_status_p->actual_size = total_size; dealloc_job_desc(jd); jd = NULL; return (ret); } /* if we fall to this point, the job did not immediately complete and * we must queue up to test it later */ *id = jd->job_id; bmi_pending_count++; return(job_time_mgr_add(jd, timeout_sec)); }

先从参数说起,省略掉的是与具体下层调用相关的参数(在第1行由上层调用传入,在第33、37行转给下层调用),而剩余的参数则基本出现在所有此类函数的定义中。参数中还需说明的是,第4行out_status_p指向一个任务状态,当本函数对应的异步调用当下即完成时,直接填写这个任务状态,告知调用者相关信息;第7行的timeout_sec是超时秒数,基于时间管理器的异步式调用函数均需此参数。除此之外,第2、3、6、8行的参数用于描述任务的相关信息,存入任务描述符(见如下步骤1)。

 

下面说明上述函数的主要行为,可分为四步:

  1. 创建一个任务描述符并保存任务相关信息(第20~29行)
    注释中也提到,这个任务描述符用户任务无法立即完成时加入相关等待队列,其他情况下可能不发挥作用而直接注销掉。
  2. 调用对应的下层函数(第30~38行),并进行错误处理(第39~47行)
    错误处理方式就是撤销任务描述符,并把错误号和相关信息通过任务状态指针out_status_p传出(第42~43行)。
  3. 处理同步的情况
    即任务当即完成,处理方式是撤销任务描述符,把相关信息通过任务状态指针out_status_p传出(第51~53行)
  4. 处理异步的情况
    即需要等待任务执行,处理方式是传出任务描述符ID,并将任务描述符加入时间管理器队列(第63行,参见job_time_mgr_add )。

还要特别说明的是job_context_id类型的参数。我们可以发现,第6行传入的context_id,只是用于在第24行记入任务描述符,并不传入下层调用。可见,该参数是用于本层往上区分任务来源(这也可以说是一种状态信息),对job层往下并不可见;而下层调用(第33、37行)只传入全局值global_bmi_context,即所有job层的BMI调用都视作来自同一个全局上下文,不加区分。

  • 基于自定义队列的异步式

此类函数与上类较为相似,但使用下层组件自备的队列及其相应的对未完成任务的处理方式,函数内不涉及job提供的队列。通过下面函数2可以看到,绝大部分代码和函数1都是对应的,出去调用底层函数不同,几乎唯一的差异就是最后(第59行)没有调用任务时间管理器的job_time_mgr_add函数。

 

函数2. job_trove_keyval_read

int job_trove_keyval_read(//...... PVFS_vtag * vtag, void *user_ptr, job_aint status_user_tag, job_status_s * out_status_p, job_id_t * id, job_context_id context_id, PVFS_hint hints) { /* post ... If it completes (or fails) * immediately, then return and fill in the status structure. * If it needs to be tested for completion later, then queue * up a job_desc structure. */ int ret = -1; struct job_desc *jd = NULL; void* user_ptr_internal; /* create the job desc first, even though we may not use it. This * gives us somewhere to store the BMI id and user ptr */ jd = alloc_job_desc(JOB_TROVE); // ...... jd->hints = hints; jd->job_user_ptr = user_ptr; jd->u.trove.vtag = vtag; jd->context_id = context_id; jd->status_user_tag = status_user_tag; jd->trove_callback.fn = trove_thread_mgr_callback; jd->trove_callback.data = (void*)jd; user_ptr_internal = &jd->trove_callback; #ifdef __PVFS2_TROVE_SUPPORT__ ret = trove_keyval_read(/*......*/ jd->u.trove.vtag, user_ptr_internal, global_trove_context, &(jd->u.trove.id), hints); // ...... #endif if (ret < 0) { /* error posting trove operation */ out_status_p->error_code = ret; out_status_p->status_user_tag = status_user_tag; dealloc_job_desc(jd); jd = NULL; return (1); } if (ret == 1) { /* immediate completion */ out_status_p->error_code = 0; out_status_p->status_user_tag = status_user_tag; out_status_p->vtag = jd->u.trove.vtag; dealloc_job_desc(jd); jd = NULL; return (ret); } /* if we fall through to this point, the job did not * immediately complete and we must queue up to test later */ *id = jd->job_id; trove_pending_count++; return (0); }

  • 同步式

同步式即通过job调用阻塞函数,函数操作完全结束时才继续执行。我们以job_dev_write_list为例,如函数2。

 

函数3. job_dev_write_list

int job_dev_write_list( // ...... void* user_ptr, job_aint status_user_tag, job_status_s* out_status_p, job_id_t* id, job_context_id context_id) { int ret = -1; /* NOTE: This function will _always_ immediately complete for now. * It is really just in the job interface for completeness, in case we * decide later to make the function asynchronous */ ret = PINT_dev_write_list(buffer_list, size_list, list_count, total_size, buffer_type, tag); if(ret < 0) { /* error posting */ out_status_p->error_code = ret; out_status_p->status_user_tag = status_user_tag; return(1); } /* immediate completion */ out_status_p->error_code = 0; out_status_p->status_user_tag = status_user_tag; out_status_p->actual_size = total_size; return(1); }

 

注释中也提到,之所以放到job的框架中,也是为日后实现异步版本提供方便(不必更改调用函数)。另外,注意到最终返回值为1,遵从job中异步函数返回值的约定:0表示函数执行成功,1表示任务已立即完成,负数表示错误码。

 

第13~14行调用实际操作函数,这里是阻塞式,等到任务完成函数返回。不论错误处理(第18~20行),还是正常流程(第23~26行),都都是写错误码(error code)及其他相关信息,并通过任务状态指针out_status_p传出;确切说,这里没有在job层保留状态信息,因为任务已经完成。

  • 基于线程管理器的异步式

此类函数与函数1也极为相似,差别也在于最后的操作。当没有意外(BMI中称作unexpected message, dev中称作unexpected receive)到达时,将等待意外任务链入全局变量dev_unexp_queue中(第58行),同时注册处理函数(第61行);与此同时,增加了两个全局计数变量的值——dev_unexp_pending_count(第59行) 和dev_unexp_count(第61行调用的函数中)。然后将后续处理工作较给线程管理器(参见src/io/job/thread-mgr )。

 

函数4. job_dev_unexp

int job_dev_unexp( struct PINT_dev_unexp_info* dev_unexp_d, void* user_ptr, job_aint status_user_tag, job_status_s * out_status_p, job_id_t* id, enum job_flags flags, job_context_id context_id) { /* post a dev recv for an unexpected message. We will do a quick * test to see if an unexpected message is available. If so, we * return the necessary info; if not we queue up to test again later */ int ret = -1; struct job_desc *jd = NULL; int outcount = 0; #ifndef __PVFS2_CLIENT__ return(-PVFS_ENOSYS); #endif /* create the job desc first, even though we may not use it. This * gives us somewhere to store the user ptr etc. */ jd = alloc_job_desc(JOB_DEV_UNEXP); // ...... jd->job_user_ptr = user_ptr; jd->u.dev_unexp.info = dev_unexp_d; jd->context_id = context_id; jd->status_user_tag = status_user_tag; /* only look for immediate completion if our flags alow it */ if (!(flags & JOB_NO_IMMED_COMPLETE)) { ret = PINT_dev_test_unexpected( 1, &outcount, jd->u.dev_unexp.info, 0); if (ret < 0) { /* error testing */ dealloc_job_desc(jd); jd = NULL; out_status_p->error_code = ret; return 1; } if (outcount == 1) { /* there was an unexpected job available */ out_status_p->error_code = 0; out_status_p->status_user_tag = status_user_tag; dealloc_job_desc(jd); jd = NULL; return (ret); } } /* if we fall through to this point, then there were not any * uenxpected receive's available (or none requested); queue up to * test later */ gen_mutex_lock(&dev_unexp_mutex); *id = jd->job_id; job_desc_q_add(dev_unexp_queue, jd); dev_unexp_pending_count++; gen_mutex_unlock(&dev_unexp_mutex); PINT_thread_mgr_dev_unexp_handler(dev_thread_mgr_unexp_handler); return (0); }

 

你可能感兴趣的:(thread,list,user,null,任务,代码分析)