前言
由于DragonOS的驱动、模块变多了,如果每个模块的初始化都手动加个函数调用的话,对条件编译非常不友好。因此我使用Rust的procmacro开发了一个库,叫做unified-init,用于统一初始化内核的模块。
原理
设计了“初始化器”和”初始化器数组“两个对象。通过在函数上方加lint,编译期自动生成初始化器,并使用linkme库,在链接时,把初始化器链接到指定的初始化器数组内。然后我们就能在某个地方统一的调用数组内所有的初始化器了。
使用
下面给了一个示例:
代码语言:javascript复制use system_error::SystemError;
use unified_init::define_unified_initializer_slice;
use unified_init_macros::unified_init;
/// 初始化函数都将会被放到这个列表中
define_unified_initializer_slice!(INITIALIZER_LIST);
#[unified_init(INITIALIZER_LIST)]
fn init1() -> Result<(), SystemError> {
Ok(())
}
#[unified_init(INITIALIZER_LIST)]
fn init2() -> Result<(), SystemError> {
Ok(())
}
fn main() {
assert_eq!(INITIALIZER_LIST.len(), 2);
}
源码解析
本文的源码解析基于
https://github.com/DragonOS-Community/DragonOS/commit/91e9d4ab55ef960f57a1b6287bc523ca4341f67a 这个版本的代码。
代码在kernel/crates/unified-init下。
macros目录是过程宏的代码,也是这个库的核心,因此本文只对macros目录下的lib.rs进行讲解。
unified_init
是 DragonOS 中的一个过程宏,它主要用于初始化操作,可以用于将一些函数注册到统一初始化列表中。它的主要工作流程是:
- 解析属性参数:unified_init 首先会解析传入的属性参数,这包括初始化列表名
INITIALIZER_LIST
和一个指向目标链表的路径initializer_instance
。 - 获取当前函数:然后,它会从输入的宏中解析出一个函数定义。这个函数需要满足特定的签名要求,即返回类型为
Result<(), SystemError>
,且没有参数。 - 检查函数签名:接下来,它会检查解析出的函数的签名是否满足要求。如果不满足,就会返回一个错误。
- 生成
UnifiedInitializer
:如果函数签名满足要求,那么它会生成一个全局变量unified_initializer
,并将其注册到目标链表中。这个全局变量是unified_init::UnifiedInitializer
的实例,用于在程序运行时初始化指定的函数。 具体来说,generate_unified_initializer
函数会生成一个类似这样的代码片段:
static unified_initializer_xxx_xxx: unified_init::UnifiedInitializer = ::unified_init::UnifiedInitializer::new("xxx", &(xxx as ::unified_init::UnifiedInitFunction));
这里的 "xxx" 是函数的全名,"xxx" 是 unified_init::UnifiedInitFunction
的别名。
整个过程宏的主要目的是为了简化初始化操作,使得多个函数的初始化操作可以统一进行,避免重复的代码。