跳转到内容

IPC 协议

OpenOS 采用基于 Channel 的同步消息传递 IPC 机制,灵感来自 seL4 和 Zircon(Fuchsia)。Channel 是双向、同步的消息管道,支持 Handle 传递。

┌─────────────┐ 消息 ┌─────────────┐
│ 任务 A │ ──────────────→ │ 任务 B │
│ (发送方) │ │ (接收方) │
└─────────────┘ └─────────────┘
│ │
▼ ▼
Handle A Handle B
(End A) (End B)
  • Channel - 双向同步消息管道
  • Handle - 不透明的能力令牌,引用内核对象
  • Rights - 10 位权限位掩码
  • Generation - 防止关闭后使用

创建一个新的 Channel,返回两端的 Handle。

fn channel_create(flags: u64) -> (handle_a: Handle, handle_b: Handle)

Handle 格式:

bits [0:31] object_id — 内核对象表索引
bits [32:47] rights — 能力权限位掩码
bits [48:63] generation — 防止关闭后使用

发送消息。阻塞直到接收方调用 channel_receive。

fn channel_send(handle: u64, msg: *const u8, len: u64) -> i64

行为:

  • 消息被复制到内核
  • 如果接收方已阻塞在 channel_receive,消息立即传递
  • 否则,发送方阻塞直到接收方准备好

接收消息。阻塞直到有消息可用。

fn channel_receive(handle: u64, buf: *mut u8, len: u64) -> u64

行为:

  • 如果已有消息,立即返回
  • 否则,阻塞直到有消息到达
  • 返回实际接收的字节数

原子性的发送 + 阻塞等待回复(RPC 原语)。

fn channel_call(handle: u64, msg: *const u8, len: u64) -> u64

行为:

  1. 发送消息
  2. 阻塞等待回复
  3. 返回回复数据

超时: 可通过 flags 参数指定超时。

回复接收到的消息。解除调用方 channel_call 的阻塞。

fn channel_reply(handle: u64, msg: *const u8, len: u64) -> i64

重要规则:

  • 必须对每个 channel_receive 调用 channel_reply
  • 如果服务器崩溃而没有回复,内核自动发送 EPIPE 错误

关闭 Handle。如果这是最后一个引用,销毁对象。

fn handle_close(handle: u64) -> i64

克隆 Handle,可选择收窄权限。

fn handle_duplicate(handle: u64, new_rights: u64) -> u64

权限收窄: 新权限 = 原始权限 & new_rights

通过 Channel 将 Handle 发送给另一个进程。

fn handle_transfer(handle: u64, target_channel: u64, rights: u64) -> i64

特性:

  • Handle 被移动——在发送方不再有效
  • 接收方获得新 Handle,权限 = 原始权限 & rights
  • 单调特权递减——权限只能减少

Channel 是同步和无缓冲的——send 阻塞直到 receive。三种模式处理并发:

// 服务器预先创建 N 个工作线程
loop {
let msg = channel_receive(handle);
let response = process(msg);
channel_reply(handle, response);
}
// 一个线程接受请求,分发到工作池
loop {
let msg = channel_receive(handle);
spawn(move || {
let result = process(msg);
channel_reply(handle, result);
});
}
// 内核内部处理(用于简单服务)
// channel_send 触发内联处理
客户端 服务器
│ │
│ channel_call(msg) │
│ ─────────────────────────────→ │
│ │ channel_receive()
│ │ 处理请求
│ │ channel_reply(response)
│ ←───────────────────────────── │
│ (解除阻塞) │
// 服务器必须回复,即使在错误时
match process_request(msg) {
Ok(response) => channel_reply(handle, response),
Err(e) => channel_reply(handle, error_response(e)),
}
// 如果服务器崩溃,内核自动回复 EPIPE
// 进程 A 有 Memory Handle (READ | WRITE)
// 想给进程 B 只读访问
let (a_end, b_end) = channel_create(0);
// A 收窄权限并传递
handle_transfer(memory_handle, a_end, READ);
// memory_handle 在 A 中无效
// B 接收 Handle
let msg = channel_receive(b_end);
let shared_mem = received_handles[0]; // 只有 READ 权限
原始 Handle: READ | WRITE | TRANSFER | MAP
传递 Rights: READ | MAP
──────────────────────────────
接收方获得: READ | MAP (交集)

每个进程有自己的服务命名空间,由父进程填充。

// 父进程
let (server, client) = channel_create(0);
endpoint_register("console", server);
// 传递给子进程
handle_transfer(client, child_channel, READ | WRITE);
// 子进程
let console = endpoint_discover("console");
channel_call(console, msg);
方面旧实现(端口)新实现(Channel)
IPC 原语端口 + 消息Channel + Handle
同步性异步发送同步会合
能力模型Handle + Rights
Handle 传递不支持支持
RPC手动实现channel_call 原语
命名空间全局端口表每进程命名空间
功能状态说明
channel_create[done] 完成创建 Channel
channel_send[done] 完成发送消息
channel_receive[done] 完成接收消息
channel_call[done] 完成原子 RPC
channel_reply[done] 完成回复消息
handle_close[done] 完成关闭 Handle
handle_duplicate[done] 完成克隆 Handle
handle_transfer[done] 完成传递 Handle
用户空间服务[done] 完成console_svc.elf
endpoint_register[plan] 计划中服务注册
endpoint_discover[plan] 计划中服务发现