异步管理
对于异步系统调用,利用Rust异步协程采用多对一线程模型。内核中有多个内核服务线程,一个内核协程执行器线程。用户线程以异步的形式向内核服务线程请求服务。用户线程通过异步系统调用的形式请求服务。通过内核主线程、内核服务线程、内核协程执行线程协作完成这一过程。本章介绍异步系统调用相关的异步协程管理。
在内核线程管理中给出了一个用户线程向内核线程发送请求直到请求完成的控制流实例。这里再次给出简述,本章围绕这个过程进行展开。
下面给出一个用户线程请求内核服务的实例过程中的控制流说明
用户线程请求服务
/// 向内核线程发送请求
pub fn sys_kthread_req(kthread: Arc<Kthread>, ctrl_ptr: usize, buf_ptr: usize) -> isize {
// 向内核服务线程发送请求
...
// 创建协程后等待
thread.state = ThreadState::Waiting;
executor::spawn(async_kthread_req(thread, kthread.clone(), req_id));
1
}
用户线程通过sys_kthread_req()
系统调用发送一个请求,之后用户线程被置为异步等待状态(不会被调度执行),然后创建一个async_kthread_req
异步协程,将其添加到内核协程执行线程的任务队列中去,等待执行。
/// sys_kthread_req函数的协程任务
async fn async_kthread_req(thread: Arc<Thread>, kthread: Arc<Kthread>, req_id: usize) {
let kthread_req_future = KthreadReqFuture::new(thread.clone(), req_id, kthread.clone());
kthread_req_future.await;
thread.state = ThreadState::Runnable;
}
协程人物创建一个KthreadReqFuture,并等待。当KthreadReqFuture就绪后(此时内核服务线程服务完成),将用户线程状态置为就绪(可调度)。
协程执行线程轮询KthreadReqFuture协程
impl Future for KthreadReqFuture {
type Output = ();
fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
...
let res_id = wait_lock_or_yield(&res_id_lock);
// 根据res_id和req_id判断请求是否完成
let ret = if *res_id >= req_id {
// 完成请求
Poll::Ready(())
} else {
// 未完成请求
kthread.add_req_waker(cx.waker().clone(), self.req_id);
Poll::Pending
};
return ret;
}
}
通过内核线程已完成请求id和当前请求id的大小关系判断当前请求是否已完成,若未完成则将当前协程的唤醒器waker添加到内核线程的waker队列中去,并返回pending。
内核服务线程完成服务并使用waker唤醒协程
下面是一个内核服务线程的代码实例
pub fn entry(ktid: usize) {
// 无限循环处理请求,请求队列为空时进入睡眠
loop {
// 保证所有堆上内存释放完毕,再进入IDEL状态
{
// 获取队首请求,不能带锁放弃CPU,否则无法向队列中添加请求
...
// 设置状态为繁忙
kthread.set_state(KthreadState::Busy);
// 完成服务
...
// 服务完成,唤醒协程
kthread.response_req(id);
}
kthread.set_state(KthreadState::Idle);
}
}
response_req已在前面说明,唤醒协程后,协程执行线程再次轮询KthreadReqFuture时即返回Ready,async_kthread_req中将用户线程状态设置为就绪(可调度),这样就完成了用户线程从发起请求到完成请求的整个过程。
下面的各部分将具体介绍异步协程和协程执行器