Skip to content

2-runtime: handoff 调度机制参照 io_uring(passthrough / 共享环 / 完成式通知 / 背压 / 预注册零拷贝) #346

Description

@miaobyte

参照对象:Linux io_uring实现机制(不是引入它)。
本 issue 只做一件事:把 io_uring 里与「跨进程/跨 runtime 派发 opcode」相关的机制逐条对到 kvlang 现有 handoff 上,给出抄什么、不抄什么,作为 #189(kvlang-runtime 概念与调度设计)的参考输入。

背景:现有 handoff 机制

myrwircaps 命中失败 → handoff_external_rwirruntime/src/kvcpu.c:881)把 pc 挂到共享队列
/lib/<opcode>/vids/<vtid>;队列由首个 rwir 建真实 strkeymap、后续同 opcode 的 vidsPtr 指向它
(幂等,runtime/src/myrwircaps.c:13);扩展 RunSeq(pc) 顺序跑自己 caps 内的连续指令,遇第一条不在
caps 内的即停、写回最终 PC、删 vids 条目;中央 runtime 用 kvlangKvWatch(vids/<vtid>, 30s)
kvcpu.c:901)等条目消失,自己不推进 PC。规范见 spec 05-runtime语义/07-notinmyrwircaps
08-myrwir扩展-c

即:队列 = 提交环、删条目 = 完成事件、opcode ∈ 谁 = 谁执行。这三个恰好是 io_uring 的核心结构。

机制逐条对照

io_uring 机制 kvlang 现状 差什么 / 抄哪条
SQ/CQ 两个 mmap 共享环;完成式(completion)而非就绪式(readiness) vids strkeymap 队列 + Watch 条目消失 结构同构;差别在介质(持久 KV 树 vs 内存环)→ 不抄介质,抄语义
IORING_OP_URING_CMD(5.19+)passthrough:内核不解释该 opcode,按 fd 找驱动注册的 uring_cmd,把 SQE 的 cmd 字段(普通环 16B,IORING_SETUP_SQE128 下 80B)原样递给驱动;opcode 由子系统/驱动自定义(连 LSM 都审不出来,故另加 hook) notinmyrwircaps handoff:中央 runtime 不解释远端 opcode,按 /lib/<opcode> 路由到扩展 同构,这是本 issue 最强的一条:kvlang 已实现 io_uring 的 passthrough 语义;可借它论证「中央 runtime 永不需要懂扩展 opcode」这一职责线
提交侧只写 SQE,完成侧单值 res + 标志(NVMe 透传要两个结果 → IORING_SETUP_CQE32 扩展只写回最终 PC;失败/部分完成无处表达 抄:把「删条目 = 完成」升级为完成槽(pc + status + 字节数/nr),删条目仅作通知。否则扩展失败只能靠 30s 超时兜
SQPOLL:内核线程自旋 SQ,提交零 syscall kvlangKvWatch(..., 30s) Watch 是粗糙的 SQPOLL(也是粗糙的完成轮询);若要降延迟,方向是「扩展完成时直接唤醒等待方」,而非缩短 watch 间隔
定长:SQ 满 → 提交侧必须等/重试(io_uring_enter 阻塞;SQPOLL 下自旋) vids 无界 strkeymap 抄:队列上限 + 满即拒绝 handoff(宁可报错不静默排队)。无界队列 = 无背压 = 内存与延迟双失控
IOSQE_IO_LINK:链起来才保证提交序=完成序;不链则完成可乱序 RunSeq「顺序执行、非并行 batch」,单消费者时天然有序 抄:明确写死「同一 vids 队列是否允许多消费者并行认领」。若允许,必须引入显式依赖/令牌;否则顺序保证是隐式的、会被并发打破
IORING_REGISTER_BUFFERS / REGISTER_FILES:预注册固定缓冲/fd,免每步 pin/map ResolveReadPath + kvspaceGet 借用读 + DecodeHead + GetPart 分片(已是零拷贝骨架) 半抄:现在是「每次借用」,缺「预注册/常驻」概念。与 #212 / #268(句柄缓存)合并评估:扩展声明常用 key/缓冲,跨多步 handoff 复用
multishot:一次提交、多次完成 一个 vthread 反复 handoff(每步一次 Set + Watch) 抄:把「一次 handoff 跑一整段」的边界说清(现在是「遇陌生即还」,可能出现乒乓 handoff)。可考虑「同 opcode 连续段合并成一次提交」
IORING_SETUP_SINGLE_ISSUER:5.19 时唯一需要它的就是 URING_CMD vids 首队列由多写者经 Ptr 统一写入 已踩过坑(重复注册经路径穿透把首队列改成自指 Ptr → resolve_path 死循环,修法=幂等跳过)。照 uring 的结论:多写者共享一个环必须显式约定所有权,否则就是竞态温床

落地方案(可执行清单)

  1. 背压vids 加容量上限(计数/条目数),满时 handoff 直接报 RuntimeError,不排队。
  2. 完成槽:handoff 结果改为写 (pc, status, bytes),条目删除只作通知;失败路径有明确表达。
  3. 所有权与接管:watch 超时后谁接管、旧执行体复活怎么办 → 引入 epoch/fencing token(写回带 epoch,过期写回被拒)。与 0.4 的 [RFC] 0-kvspace: XValue head 增加 rwx + uid/gid 权限系统:kvspace 层硬强制读写码 #54(权限系统)同源,建议合并设计。
  4. 顺序规则显式化:写进 spec —— 同一 vids 队列允许/禁止并行认领,以及禁止时的实现约束。
  5. 预注册零拷贝:扩展跨步复用常驻缓冲/常用 key(与 [EPIC] 2-runtime: 性能加速下一步计划:kv.set/get 重构 + kvspace shm 零拷贝 + kvspace-c 优化 #212[PERF] 0-kvspace/2-runtime: 句柄缓存(kvspaceResolveRef/GetByRef/SetPartByRef + block_id + forwarding tombstone + 帧级失效) #268 合评)。
  6. 事件不丢:Watch 目前「删即信号」,丢一次就等 30s → 参考 KV watch 的 revision + 从指定版本重放(kvspace 层缺口,另记)。

明确不做

  • 不引入内核 io_uring 依赖,不把 vids 换成共享内存环:持久可恢复是 kvlang 的红线(PC 是 KV 路径、崩溃可续跑),io_uring 只作机制参照。
  • 不改「中央 runtime 不解释扩展 opcode」这一职责线(它本来就对)。

验收

参考

关联:#189(调度设计)、#54(权限/epoch 可合并)、#212 / #268(预注册零拷贝合评)、#305(handoff 相关指针语义已落)。

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    RFC需要讨论与决策的设计提案(含裁决记录)

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions