sel4 ipc延迟回复机制 #
sel4 的进程之间通过 Endpoint 和 Notification 来通信,在 rel4-linux-kit 中的消息是同步的,因此当 client1 发送一个消息到 server 的时候,client1 会阻塞,而 server 此时 reply cap 的状态对应 client1,这个时候就无法接收第二个 client 发送的 ipc, 如果舍弃这个去等待第二个 client 的 ipc, 那么 client1 就会丢失 reply cap 进入无休止的等待。
而服务程序经常会遇到无法立即回复的情况(比如等待资源或完成任务),所以必须延迟回复。为了解决这个问题, rel4-linux-kit 中使用了 IpcSaver 机制并形成了一个 mod,这个机制利用了 sel4 可以将 reply cap 保存在一个指定的 slot 的机制,实现了简单的延迟回复机制。
IPCSaver #
/// 保存未能及时回复的 IPC
#[derive(Debug)]
pub struct IpcSaver {
/// 等待队列
queue: VecDeque<LeafSlot>,
/// 闲置的 slot
free_slots: Vec<LeafSlot>,
}
上述代码构建了一个简单的结构,第一个 field queue
是等待的队列,这个队列中每一个元素都是一个 reply cap。第二个 field free_slots
是回收的空 slot,reply cap 被使用后就会被清空,然后保存在这里以便下次使用。
/// 保存一个调用者的回复能力
pub fn save_caller(&mut self) -> Result<(), sel4::Error> {
let slot = match self.free_slots.pop() {
Some(slot) => slot,
None => alloc_slot(),
};
slot.save_caller()?;
self.queue.push_back(slot);
Ok(())
}
当程序需要保存 replier cap 的时候就调用上述的函数,在程序初始化的时候其中还没有 slot,这个时候就需要申请新的 slot 来用于保存,然后调用 sel4 中的 syscall 将 reply cap 保存在这个 slot 中。当程序完成某个条件的时候就会调用 reply_one
来实现回复。
/// 回复一个 Endpoint
///
/// - `msg` [MessageInfo] 需要回复的消息
pub fn reply_one(&mut self, msg: MessageInfo) -> Result<(), sel4::Error> {
let reply_cap = self.queue.pop_front();
if let Some(slot) = reply_cap {
Endpoint::from(slot).send(msg);
self.free_slots.push(slot);
}
Ok(())
}
上述代码,就是程序在完成某个条件的时候进行回复的机制,虽然这里最后采用的是 Endpoint 对象的接口,但是事实上这里并不是一个真正的 endpoint, 而是一个 reply cap。
使用场景 #
这个使用场景多用于中断和异步相关的场景,比如单独的串口程序,有多个任务都在等待输入的时候。