文章摘要
本文是“如何在内核中编写Rust”系列的第三部分,主要讨论了Rust与C在内核中的接口发展。文章指出,任何复杂的Rust驱动程序都会使用这些接口,涉及内存分配、处理不可移动结构、与锁交互等任务。虽然存在许多子系统特定的绑定,但本文重点概述了所有内核Rust代码可能使用的通用绑定。Rust通过外部函数接口(FFI)调用C代码,但直接让Rust调用内核C函数存在一些问题,如__always_inline函数、非惯用API等,尤其是在内存释放和锁处理方面,C和Rust有不同的方法。早期规划阶段,项目提出了相关解决方案。
文章总结
文章主要内容总结
本文是《如何在内核中编写Rust代码》系列的第三部分,主要探讨了Rust与C在内核中的接口设计,特别是Rust代码如何与内核的C代码进行交互,并详细介绍了内存分配、自引用结构和锁等关键主题。
1. Rust与C的接口设计
- Rust代码可以通过外部函数接口(FFI)调用C代码,但直接调用内核C函数存在一些问题,如
__always_inline函数、非惯用API等,尤其是内存释放和锁的处理方式不同。 - 内核开发团队提出为每个子系统提供集中化的Rust绑定,虽然这增加了Rust程序员的工作量,但随着更多绑定的编写,这种需求会逐渐减少。这种方法的优势在于提供了标准化的Rust接口,所有文档集中在一个地方,便于学习和理解。
2. 内存分配
- Rust默认将局部变量放在栈上,但内核栈大小有限,因此堆分配是必要的。内核中的Rust代码使用
kernel::alloc模块进行内存分配,支持Kmalloc、Vmalloc和KVmalloc三种分配方式,分别对应物理连续内存、虚拟连续内存以及两者的混合分配。 - 分配器实现了
Allocator接口,类似于用户空间的Allocatortrait。Rust提供了KBox、KVBox等快捷别名,方便使用。内存分配时可以通过kernel::alloc::flags指定详细的分配标志。
3. 自引用结构
- 内核中有许多自引用结构(如双向链表),Rust通过“Pin”机制来标记这些不可安全移动的结构。
Pin类型确保这些结构不会被意外移动,从而避免内存损坏。 - 为了简化大结构的分配,Rust API提供了
pin_init!()和try_pin_init!()宏,帮助构建自定义初始化器。
4. 锁机制
- Rust用户空间代码通常将锁与数据封装在一起,而内核的C代码则倾向于将锁与数据分离。Rust内核API提供了
LockedBy和GlobalLockedBy类型,利用Rust的生命周期系统确保在访问数据时持有特定的锁。 - 当前Rust绑定支持自旋锁、互斥锁和RCU锁,所有锁都通过
lockdep类键创建,确保锁的使用符合内核的锁验证机制。
5. 安全接口的设计目标
- 引入Rust绑定的最终目的是让更多的错误在编译时被发现。虽然机器检查的规则无法捕捉所有错误,但Rust的接口设计显著减少了运行时错误的可能性。
本文最后提到,下一篇文章将重点关注审查Rust补丁时需要注意的事项,这可能是该系列的最后一篇。
评论总结
没有有效评论