分类:
2011-02-16 15:17:19
为了高效的调度各个RPC请求,Linux的PRC调度实际上是一个事件驱动模型。C/S结构,大多使用多进程服务模型,这种模型的优点是编程简单,因为操作系统都是基于进程调度的,可以直接使用操作系统的接口。缺点是不适用于大规模的服务。服务进程或者线程的数目越多,用于切换调度的开销就越多,一旦进程或者线程的个数超过一定值,系统就会变的响应异常缓慢。而事件驱动模型正好相反,由于缺乏通用的事件调度接口,只有全部由自己实现,但是不管遇到多少数量的服务,都不会在切换上花费太多开销。
为了没有切换开销,简单点当然是完全由一个进程单干。更高级的做法是每个处理器一个进程,同时跑,多彻底。内核里面的工作队列机制实际上就是这样干的,因此RPC对于异步处理的调度就是使用的工作队列的接口。
rpc在一个函数中处理了同步和异步两种情况。事实上,如果是同步情况,则会在该函数中睡眠,直到醒来的条件满足。如果是异步情况,则处理完后会直接返回,任务的睡眠和后续启动由专门的异步机制,也就是工作队列来完成
的。
在linux内核中RPC任务调度的代码位于net\sunrpc\sched.c中,具体的函数是_rpc_execute().
函数名称:static void __rpc_execute(struct rpc_task *task)这是一个RPC的调度器,更准确的说,是采用有限状态机。
for(;;)
{
if (task->tk_callback) save_callback(task)//当前任务是否有回调函数,有则处理
if (!RPC_IS_QUEUED(task)) task->tk_action(task)//处理当前任务
if (task_is_async)
out_of_line_wait_on_bit(&task->tk_runstate,RPC_TASK_QUEUED, rpc_wait_bit_killable,TASK_KILLABLE);//异步任务处理,则返回,同步任务则进入睡眠状态。 rpc_set_running(task) //一些其他的处理。
}
rpc_release_task(task)//处理完成后清除相关的资源。
该函数被rpc_execute和 rpc_async_schedule函数调用。最终rpc_async_schedule通过rpc_make_runable函数被rpc_execute函数调用。具体代码为:
void rpc_execute(struct rpc_task *task)
{
rpc_set_active(task);
rpc_make_runnable(task);
if (!RPC_IS_ASYNC(task))
__rpc_execute(task);
}
通过调用rpc_execute函数来实现对RPC任务的调度。这个函数一般是被递归的调用。
这些关于调度的函数是通过工作队列机制来实现的。具体是在rpc_make_runable函数中调用的。这里采用了一个叫做rpciod_workqueue的工作队列来完成。
工作队列的介绍 1。默认情况下内核只有名字为event的一类工作队列,这一类工作队列在每个cpu上都有一个内核线程。RPC新建了一类,取名为rpciod工作队列。 2。随后没个rpc任务初始化时候都会有 3。当一个task启动时候,如果是异步的,便启动函数rpc_async_schedule()作为异步rpc请求的处理函数。
wq = create_workqueue("rpciod");
if (wq == NULL) {
// errorr process
}
rpciod_workqueue = wq;
task->tk_workqueue = rpciod_workqueue;
INIT_WORK(&task->u.tk_work, rpc_async_schedule, (void *)task);
status = queue_work(task->tk_workqueue, &task->u.tk_work);
参考:linux rpc结构的一种事件驱动的状态处理
linux内核2.6.37\net\sunrpc\sched.c