操作系统嵌入式嵌入式OS【免费下载链接】tockA secure embedded operating system for microcontrollers项目地址https://gitcode.com/gh_mirrors/to/tock点击查看免费下载导读本文基于 Tock 核心工作组Core WG2025 年 10 月 2 日的会议纪要doc/wg/core/notes/core-notes-2025-10-02.md深入解读本次会议敲定的两项关键技术决策以SingleThreadValue逐步替换static mut的 panic 基础设施迁移路线以及为tock-registers引入外部依赖的例外政策。文章将结合当前仓库中 kernel/src/utilities/single_thread_value.rs、kernel/src/platform/chip.rs、kernel/src/deferred_call.rs 与 doc/ExternalDependencies.md 等源码与文档还原讨论背景、实现原理与最终结论帮助读者理解 Tock 如何在不牺牲安全性的前提下推进内核现代化。会议概览与议程本次会议由 Amit Levy、Brad Campbell、Branden Ghena、Hudson Ayers、Johnathan Van Why 与 Leon Schuermann 六位核心工作组成员参加议程集中在三个相互关联的话题上Cargo.lock PR#4605合并进度与构建系统同步问题SingleThreadValue#4519static mut的替代方案如何推广到全部板卡尤其是 panic 基础设施外部依赖政策#4616 与 #4589为tock-registers成为外部依赖而修改政策的两个竞争提案。这三者共同指向一个更大的目标——解除 Tock 对旧式static mut的依赖、跟上 Rust 工具链的演进同时为内核引入受控的外部依赖打开口子。Cargo.lock PR#4605为外部依赖铺路的构建系统同步讨论要点会议首先确认了 Leon 提交的 Cargo.lock PR#4605可以合并。该 PR 之所以需要尽快合并是因为lockfile 必须与 Tock 的构建系统保持同步——一旦仓库内 crate 版本发生变动Cargo.lock也会随之变化长期不合并会产生大量冲突。关于 Cargo.lock 是否与其他文件绑定的问题会议结论是历史上它们曾相互关联但当前已经彼此独立。更重要的是Leon 指出引入外部依赖之后必须能够精确控制依赖版本这正是 Cargo.lock 的职责所在。为什么 Cargo.lock 对 Tock 如此关键Cargo.lock 位于仓库根目录Cargo.lock锁定了工作区所有依赖含传递依赖的精确版本与校验哈希。在会议讨论中Johnathan 与 Brad 进一步强调了它的安全价值cargo 会对依赖进行哈希校验比单纯的 git 哈希更强cargo 的版本解析还会考虑传递依赖transitive dependencies保证整个依赖树的一致性。从 Cargo.toml 的[workspace.dependencies]可以看到tock-registers { version 0.10.0, default-features false }已作为工作区级依赖出现这正是外部依赖讨论落地后的直接产物。可以推断合并 #4605 是让外部依赖版本可控、可审计的第一步。SingleThreadValue#4519替换static mut的迁移工程背景panic 基础设施为什么成为瓶颈Brad 在会上点明了问题的核心panic 基础设施的代码被复制到了几乎所有板卡如今要更新它工作量巨大。这里的panic 基础设施指的是各架构、各板卡为打印 panic 信息而维护的全局状态。更紧迫的约束是只要还存在static mutTock 就无法升级 Rust 工具链。因为新版 Rust 对static mut的引用提出了更严格的借用规则引用static mut在安全代码中不再被允许而半途而废的迁移会阻塞整个版本的发布。从当前仓库可以印证这一判断static mut仍存在于 arch/cortex-m/src/syscall.rs如SYSCALL_FIRED、SVC_SWITCH_TO_APP、APP_HARD_FAULT、SCB_REGISTERS等全局状态、arch/x86/src/interrupts/poller.rs 的SINGLETON以及 arch/riscv/src/lib.rs 的链接器符号_szero、_ezero等。这些正是迁移工作尚未完成的部分。SingleThreadValue 的设计动机SingleThreadValuekernel/src/utilities/single_thread_value.rs是本次迁移的核心替代品。它的设计目标是在不要求T: Sync的前提下把非Sync的值安全地放进全局静态变量。其文档明确说明了取舍逻辑# Implementation Trade-Offs一节自动化执行优先于代码审查Tock 一贯主张用 Rust 的类型系统与 API 强制正确性即使检查发生在运行时运行时检查而非编译期检查截至 2025 年 7 月社区尚不知道如何把这些检查移到编译期接受忘记绑定即失败的接口缺陷类型创建后必须显式绑定到线程类似 Tock 常见的先建对象、后设回调模式漏掉这一步会导致运行时检查永远失败但这种代价换来了可保证的健全性soundness。核心实现三阶段绑定状态机SingleThreadValueT内部持有三个字段value: UnsafeCellMaybeUninitT——被包裹的值构造时不初始化以避免T: Send约束bound_to_thread: AtomicUsize——绑定状态的原子指示器其取值对应BoundToThreadStage枚举thread_id_and_fn: UnsafeCellMaybeUninit(fn() - usize, usize)——记录查询当前线程 ID的函数指针与已绑定的线程 ID。绑定过程是一个严格递增的三阶段状态机#[repr(usize)]枚举见源码第 29-64 行阶段值含义Unbound0尚未绑定thread_id_and_fn未初始化可以被绑定Binding1已通过compare_exchange原子操作预留初始化权防止并发绑定thread_id_and_fn仍未初始化Bound2已绑定到某线程thread_id_and_fn初始化完毕且封存不可再修改两个绑定入口bind_to_threadP: ThreadIdProvider安全方法通过compare_exchange(Unbound → Binding)原子地抢占绑定权依赖cfg(target_has_atomic ptr)即目标平台需支持usize级别的原子比较交换bind_to_thread_unsafeP: ThreadIdProviderunsafe 方法适用于不支持compare_exchange的平台要求调用者在外部保证不会与其他绑定调用并发因此可直接从Unbound跳到Bound省去中间状态。两个方法均在完成thread_id_and_fn与value的初始化后以Ordering::Release将状态存储为Bound确保后续Acquire加载能观察到完整的初始化写入源码第 342-348、439-445 行。访问入口是get() - OptionT内部先调用bound_to_current_thread()该方法以Acquire顺序加载bound_to_thread确认状态为Bound后读取封存的线程 ID并与ThreadIdProvider::running_thread_id()的当前返回值比对一致才返回Some(T)否则返回None。由于只允许同一线程获取共享引用多线程下通过Cell、MapCell等内部可变性容器再获取可变访问这正是 tock-cells 中MapCell/TakeCell的用武之地。类型整体通过unsafe implT Sync for SingleThreadValueT {}标记为Sync源码第 206 行其 SAFETY 注释给出的理由是该类型在运行时强制值只对起源线程可见与标准库LocalKey的语义类似。ThreadIdProvider线程识别的安全前提SingleThreadValue的安全性完全建立在ThreadIdProvider的正确实现之上。该 trait 定义于 kernel/src/platform/chip.rs 第 128-137 行pub unsafe trait ThreadIdProvider { /// Return a unique ID for the currently executing thread. fn running_thread_id() - usize; }它是一个unsafe trait理由很直接实现者必须保证返回的 ID 对当前执行线程唯一且一致否则依赖它的SingleThreadValue会变得不健全。文档同时指出嵌入式平台虽是单核、单执行线程但中断服务例程ISR的执行构成了第二条执行线程因此实现至少要能区分主线程与ISR 上下文。ThreadIdProvider通过Chiptrait 的关联类型type ThreadIdProvider: ThreadIdProvider与具体芯片绑定kernel/src/platform/chip.rs 第 26 行即由各架构/芯片 crate 提供实现。已完成的示例deferred_call 与 debugBrad 在会上强调我们已经有两个示例——一个使用 UART 的简单示例和一个使用 USB 协议栈的复杂示例。仓库中可确认的落地示例有两个示例一deferred_call 子系统kernel/src/deferred_call.rsstatic CTR: SingleThreadValueCellusize SingleThreadValue::new(); static BITMASK: SingleThreadValueCellu32 SingleThreadValue::new(); static DEFCALLS: SingleThreadValue[OptionalCellDynDefCallRefstatic; 32] SingleThreadValue::new();并配套初始化函数initialize_deferred_call_stateP: ThreadIdProvider()及其_unsafe变体第 174-196 行在启动阶段依次绑定三个全局状态。DeferredCall::new()通过CTR.get()读取计数器分配唯一 ID若get()返回None即板卡未先调用初始化函数会直接 panic 并提示DeferredCall state not initialized——这正是文档中忘记绑定即失败设计取舍的实例。示例二debug 子系统kernel/src/debug.rspub static DEBUG_GPIOS: SingleThreadValueMapCellstatic [static dyn hil::gpio::Pin] ... static DEBUG_WRITER: SingleThreadValueMapCellstatic dyn DebugWriter ... static DEBUG_WRITER_COUNT: SingleThreadValueCellusize ...同样提供initialize_debug_gpioP/initialize_debug_gpio_unsafeP与initialize_debug_writer_wrapperP/_unsafe两组初始化入口第 483、498、588、607 行。迁移策略讨论测试、移植指南与增量路线会议的核心分歧在于迁移节奏Brad 的观点两个示例已经足够不需要再等更多示例只要还有static mut就无法升级 Rust、无法发布版本且目前缺乏跟踪迁移进度的手段Leon 的观点#4519 PR 使用了更新后的接口同时覆盖 ARM 与 RISC-V尚未充分测试需要确认 panic 处理程序在所有可达路径上都正常工作避免意外破坏他主动提出在 RISC-V 平台上完成硬件测试Amit 的折中方案Leon 继续按计划做硬件测试同时 Amit 自己投入时间研究如何让迁移增量进行并形成书面策略Hudson 补充了流程建议——Leon 完成硬件测试后把 PR 从 draft 状态转正、合并再由该 PR 充当其他板卡的迁移模板Johnathan 的提议能否把 panic 处理程序实现通过宏macro搬进 kernel crateLeon 基于在 Arty 与 QEMU 上的实践经验回应各架构的 panic 实现存在大量细微差异很难简单地统一抽取最终共识尝试先抹平elide架构差异再增量恢复由 Leon 与 Amit 按该策略推进与会者普遍同意。会议还确认了一个现实的痛点由于板卡需要跟踪 kernel crate 的改动迁移可能无法按板卡逐个增量提交而需要一个大型 PR 一并完成——Brad 称之为现实情况。外部依赖政策#4616 与 #4589为 tock-registers 开放例外两项竞争提案的取舍Amit 提出了两个竞争提案要求各 PR 作者为自己的方案辩护Brad 的 #4616不改变整体政策仅添加一条针对tock-registers的、非常明确的例外条款并解释为什么它是例外Leon 的 #4589提出一条兜底规则catch-all rule允许枚举之外的外部依赖也按类似标准进入。Leon 在听完 Brad 的方案后主动撤回了自己的提案理由是#4616 是他提案的子集枚举例外依赖的方式没有问题而兜底规则我们并不需要。Branden 以样本量为 1表示保留意见Leon 接受。最终 Amit 提议关闭 #4589、按原样批准合并 #4616Hudson 表示已批准Leon 随即关闭了自己的 PR。讨论中澄清的边界如何依赖不在政策范围内Leon 指出 #4616 没有讨论依赖以何种方式被引入git 依赖 vs crates.io vs submodule 等。Johnathan 认为这是工程问题而非政策问题Hudson 也表示认可因此政策刻意排除了这一维度与 Cargo.lock 的耦合Brad 提醒 cargo 会校验依赖哈希Leon 补充 cargo 的校验强于 git 哈希因为它还覆盖传递依赖再次印证了前面 Cargo.lock 讨论的必要性。政策背景与例外理由Tock 的总体政策是内核不引入 Rust 标准库之外的外部依赖详见 doc/ExternalDependencies.md动机是便于审计只审查 Tock 仓库内的代码即可评估安全性尤其是unsafe的使用依赖树不可控外部 crate 通常自带依赖而 cargo截至 2023 年 5 月缺乏审计、禁止依赖树中unsafe的健全工具影响面分级外部依赖加在kernel/、arch/、chips/等被反向依赖的 crate 上影响远大于加在板卡 crate 上因此政策按 crate 类型逐级放宽。tock-registers是唯一被明确批准的内核级外部依赖例外其理由包括促进更严格的向后兼容考虑独立仓库避免破坏性变更藏在 Tock 专属 PR 中鼓励定期发布独立版本追踪让更广泛的社区受益降低外部用户参与门槛有明确的问题与提案入口鼓励独立贡献明确其独立项目地位。例外条款同时限制了tock-registers自身的依赖允许对syn、quote、proc-macro2的依赖理由过程宏需要解析/生成非平凡 Rust 代码且这些 crate 是 Rust 生态的核心机制、子依赖仅unicode-ident一个且无传递依赖以及仅用于单元测试的 dev-dependencies。值得注意的是该政策还列出了另一个例外flux-rs精化类型形式化验证仅作可选测试依赖但本次会议讨论聚焦于tock-registers。落地现状印证从当前仓库的 Cargo.toml 可以看到tock-registers已作为[workspace.dependencies]中的外部依赖版本0.10.0default-features false被引入且仓库根目录存在 Cargo.lock。这印证了本次会议决策已实际落地Tock 内核在零外部依赖的底线上为自维护的高价值 crate 开了一条受控、明确、可审计的例外通道。会议结论与后续行动综合三部分讨论本次会议形成了以下明确结论Cargo.lock#4605立即合并保持与构建系统同步为外部依赖的版本控制铺路SingleThreadValue#4519Leon 在 RISC-V 硬件上完成测试、验证 ARM 侧无回归后将 PR 转正并合并由该 PR 作为其他板卡的迁移模板Amit 负责研究增量迁移策略并书面化尝试先抹平各架构 panic 实现的差异再增量恢复外部依赖政策关闭 #4589按原样合并 Brad 的 #4616——以明确例外而非兜底规则的方式为tock-registers打开政策口子。延伸阅读会议纪要原文doc/wg/core/notes/core-notes-2025-10-02.mdSingleThreadValue完整实现与文档kernel/src/utilities/single_thread_value.rsThreadIdProvidertrait 定义kernel/src/platform/chip.rs迁移示例一deferred_callkernel/src/deferred_call.rs迁移示例二debugkernel/src/debug.rs外部依赖政策全文doc/ExternalDependencies.md工作区依赖声明Cargo.toml尚未迁移的static mut现场arch/cortex-m/src/syscall.rs、arch/x86/src/interrupts/poller.rs、arch/riscv/src/lib.rs赞分享操作系统嵌入式嵌入式OS【免费下载链接】tockA secure embedded operating system for microcontrollers项目地址https://gitcode.com/gh_mirrors/to/tock点击查看免费下载相关推荐Tock 核心工作组 2025-09-10 会议纪要解读LLM 贡献政策、tock-registers 外部化与 SingleThreadValueTock 核心工作组 2025 09 10 会议纪要解读LLM 贡献政策、tock registers 外部化与 SingleThreadValue 导读 本操作系统嵌入式嵌入式OSanarlog 桌面端 SQLite 响应式 UI 实战useDrizzleLiveQuery 六大稳定模式与反模式清单anarlog 桌面端 SQLite 响应式 UI 实战useDrizzleLiveQuery 六大稳定模式与反模式清单 本文是一份面向 anarlog 桌面操作系统嵌入式嵌入式OSTock 网络工作组 2025-02-10 会议纪要解读Ethernet Datapath HIL、StreamingProcessSlice 迁移与 libtock-rs 路线讨论Tock 网络工作组 2025 02 10 会议纪要解读Ethernet Datapath HIL、StreamingProcessSlice 迁移与 lib操作系统嵌入式嵌入式OS上一篇Ultimate Vocal Remover 人声分离与伴奏提取5 步跑通 UVR5 的完整指南下一篇Fay框架代码注释覆盖率检查提升文档质量的完整指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考