如何在#[no_std]环境中使用Tokio Reactor?

4
我正在尝试在Tock OS嵌入式操作系统上实现futures。我试图在#[no_std]环境中使用Tokio
我的Cargo.toml文件如下:
[package]
name = "nrf52dk"
version = "0.1.0"
authors = ["Tock Project Developers <tock-dev@googlegroups.com>"]
build = "build.rs"

[profile.dev]
panic = "abort"
lto = true
opt-level = "z"
debug = true

[profile.release]
panic = "abort"
lto = true
opt-level = "z"
debug = true

[dependencies]
cortexm4 = { path = "../../arch/cortex-m4" }
capsules = { path = "../../capsules" }
kernel = { path = "../../kernel" }
nrf52 = { path = "../../chips/nrf52" }
nrf5x = { path = "../../chips/nrf5x" }
futures = {version = "0.2.0", default-features = false }

这段代码没有错误,但是当我添加 tokio-reactor = "0.1.1" 后,出现了错误:error[E0463]: can't find crate for std。我理解这是因为Tokio从std库中导入了一些内容。

有没有办法解决这个问题呢?

2个回答

5
据我所知,你不需要。Tokio Reactor 0.1.1 从标准库导入了许多东西,没有任何条件。大部分导入可能可以切换到libcore替代方案,但是Arc需要内存分配,这在alloc crate中实现。
作为支持no_std的crate示例,请查看Futures 0.1.20。这具有一个功能标志来选择需要标准库的功能。
如果您希望这样做,您需要为Tokio及其所有依赖项做出实质性的努力,以添加功能标志以选择需要标准库的所有功能。最好向维护者开启一个问题以协调这样的工作。

1
"futures" 也会错过许多功能(例如impl <T:Notify+'static> From <Arc <T>> for NotifyHandle),而“no_std”中的“mio”也会变得很复杂。对于自己的需求编写自己的执行器可能更容易一些。 - Stefan
四年后,这仍然正确吗?博客文章https://ferrous-systems.com/blog/stable-async-on-embedded/说Rust异步很快将可用于no_std。尽管如此,它并没有在任何地方提到Tokio。Tokio是否也支持no_std?还是我需要使用其他执行器?或者这只是一个理论上的讨论,因为目前(尚)没有适用于no_std的执行器? - Bruno Rijsman
@BrunoRijsman async 在嵌入式设备上运行良好。Tokio 不行。例如,我为特定的嵌入式设备编写了自己的执行器。大多数嵌入式执行器将与平台紧密绑定,因为它们使用中断等工具。 - Shepmaster
@Shepmaster 有没有针对常见嵌入式平台(例如Cortex Mx)的公共(开源或商业)执行器可用,还是需要自己实现一个执行器? - Bruno Rijsman
@BrunoRijsman 我自己没有使用过。也许可以考虑使用 embassy - Shepmaster
@Shepmaster 感谢您提供大使馆的参考。看起来非常有前途。 - Bruno Rijsman

4
扩展Shepmaster的说法:您不需要使用tokio;它是基于mio构建的,而mio在内核中不太可能工作,特别是没有堆分配/ std的情况下。
那么,在这种环境中如何驱动任务(产生的Future)(此文针对futures 0.1.x系列编写):
  • 你的“执行器”(主循环)需要为每个任务跟踪一些状态,例如是否需要轮询它,可能还需要一些链表来查找需要轮询的任务。
  • 你需要一个地方存储这些状态;你还需要存储在 Spawn<...> 中封装的 Future。可以使用“静态”分配的存储来实现。
  • 你需要实现 UnsafeNotify(以及基础特性Notify),可能需要使用原始指针/&'static引用到任务(包括状态);notify需要能够以线程安全的方式将任务排队以被轮询。由于您将使用静态分配,因此{clone,drop}_{raw,id}函数可以为空。如果主循环正在睡眠,则notify还需要安排主循环。队列本身也需要一些全局状态(“列表头+尾”);如果您需要不同的队列,可以在NotifyHandle中存储对它的引用(例如在id: usize参数中)。
  • 您甚至可以尝试在同一“轮询队列”上运行多个循环,祝您好运,使其线程安全 :) future-0.2的 ThreadPool 可能会提供一些如何实现这一目标的思路(或者使用tokio-threadpool crate)。
  • 您可能需要为事件循环添加一些“定时器”处理;定时器应该存储一个NotifyHandle指向它应该在超时时唤醒的任务,一些状态来跟踪超时是否发生,以及事件循环需要一个活动(指针)定时器列表来确定等待时间。 (tokio-timer crate可能会给您提供某些实现思路)
  • 类似地处理异步IO; 在用户空间中,您将使用带有超时的select(或特定于平台的优化版本),在内核中,您可能需要找到其他方式:)(在tokio世界中,由基于mioReactor 提供此功能)
  • 为了驱动任务,你应该使用poll_future_notify
在 futures-0.2 中,NotifyHandle 变成了 WakerUnsafeNotify 变成了 UnsafeWake;去掉了 id: usize 上下文(只需使用一个包含您需要实现 UnsafeWake 的所有数据的结构)。不再为需要的未来存储 Spawn<...>,而是需要为每个任务手动存储 LocalMap,然后用它创建一个使用Context::without_spawn 的上下文,最后将其传递给 Future::poll

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接