rust no_std+alloc稳定和使用演示
前言
这篇文章好些天前就想写了, 因为之前一直在追踪stable rust在no_std环境下使用alloc相关的github issue, 前段时间 #102318 这个issue正式合并了, 相关的改动将在rust 1.68中稳定.
no_std下可以使用alloc一方面可以让一些裸机firmware或操作系统内核可以使用堆内存. 另一方面, 一些嵌入式linux的library/app的开发也可以使用no_std + alloc来做, 因为一些嵌入式linux经常在memory和flash空间都比较小的环境, 这种环境如果不使用no_std会导致编译出来的elf过大, no_std + alloc就可以解决这个问题, 这样能使用rust来写一些lib/app. 但是在之前想要使用alloc, 必须依赖unstable的alloc_error_handler feature, 导致其不能在stable上使用.
在stable rust 1.68之后, 当在no_std环境下, alloc失败时默认会直接panic, 不再需要alloc_error_handler. 在stable rust就可以编写no_std + alloc的rust crate了. 这相当于有了在生产环境使用这种模式编写应用的基础了. 下面我们来演示下用no_std + alloc写linux app的方法
no_std linux app
首先安装libc, 另外我们使用libc-print crate来进行println演示, 你也可以自己实现print macro
1 | $ cargo add libc --no-default-features |
cargo.toml配置在panic时直接abort, 这样不需要使用unstable的lang item, panic直接abort也符合常规的嵌入式linux的用法:
1 | [profile.dev] |
编写代码:
1 |
|
这样就可以编译出所需的app了, 文件的体积也非常小. 写rust代码大多数情况下会比用c舒服很多. 就算是在no_std的情况下也是.
no_std + alloc linux app
如果还需要使用alloc, 则我们在引入一个crate, 或者也可以自己去实现一个基于libc的global allocator, 这里为了方便演示还是使用extern crate:
1 | cargo add libc_alloc |
之后编写如下代码, 代码演示了alloc的使用方法:
1 |
|
我们需要使用global_allocator过程宏引入一个全局的分配器, 然后我们需要显式的用extern crate alloc
来引入alloc crate, 对于标准库里面的crate, cargo不能自动帮忙引入, 所以需要手动引入. 另外Box, Vec这些在std下的preclude在no_std下是不会preclude的, 所以需要手动导入才能使用.
以上就是no_std + alloc的用法. 相信随着rust 1.68的到来, rust在嵌入式领域会有很多crate慢慢的都不再需要nightly编译器了.
2023-01-15更新:
由于#106045的合并, 导致linux环境下no_std + alloc会编译失败, bare-metal环境不受影响. 目前提了一个新的issue: #106864, 不确定什么时候能合并. 不知道是否会影响到1.68上实现这个功能.
2023-01-20更新:
根据和rust开发者在#106864中的沟通可以发现, 之前在OS based toolchain环境下no_std + alloc可以编译通过存在一些幸运, 这是连接器的GC帮忙把一些不应该引入的eh_personality
去掉了, 所以编译通过了. 但是当做一些进一步的测试就能发现, 就算是之前可以编译过的nightly, 引入某些code之后, 也会编译失败. 后续可能需要等rust开发者有时间了之后再来修正这个问题了. 在stable下写有heap支持的linux的嵌入式软件似乎还得再等等了. 具体要等多久就不清楚了. unstable环境下只需要用cargo的build-std功能重新编译libcore是可以编译通过的.