依赖问题
由于ArceOS自身的unikernel架构,不同模块需要保持一定的依赖关系,从而可以方便地通过条件编译等操作来解耦某些模块,使用某些指定的模块来启动内核,从而增强OS的泛用性。
查看项目的依赖关系¶
项目的依赖关系可以通过对应的toml配置文件进行查看。如下列为axmem模块的toml:
# modules/axmem/Cargo.toml
[dependencies]
log = "0.4"
axhal = { path = "../axhal", features = ["paging"] }
axalloc = { path = "../axalloc" }
axconfig = { path = "../axconfig" }
axerrno = { path = "../../crates/axerrno" }
axfs = { path = "../axfs" }
axio = { path = "../../crates/axio" }
spinlock = { path = "../../crates/spinlock" }
xmas-elf = { path = "../../extern_crates/xmas-elf-0.9.0" }
riscv = { path = "../../extern_crates/riscv-0.10.1" }
page_table_entry = { path = "../../crates/page_table_entry" }
以上就可以看出axmem依赖了axhal/axfs/axconfig等模块。
循环依赖问题¶
而Starry虽然是宏内核架构,但仍然保持了这一泛用特性,但这也为我们开发带来了一些问题,即循环依赖问题。
由于ArceOS的模块化设计,不同的modules之间会形成以module为单位的依赖关系,相较于以文件为 单位的依赖关系而言更容易产生循环依赖的问题。
一个例子:假如一个项目中有A、B、C三个文件,A依赖B、B依赖C,不会有任何问题;但如果三 个文件被解耦到两个不同的项目 M和N中,M中有A和C,N中有B,那么M和N之间就会发生相互依 赖。 这种情况在我们的开发过程中并不少见。
当前Starry的模块依赖图如下:

如axhal需要定义trap入口,而trap实现需要很多模块的支持如axmem的地址空间等,此时就可能出现循环依赖的情况,即axhal依赖于axmem,而axmem依赖于axhal。
为了解决这个问题,有以下几种方法:
-
优化结构设计,即尽可能将实现的功能进行划分,如地址空间内容独立出来放在axmem,而不是和进程控制一起放在axprocess。
-
通过ArceOS提供的模块crate_interface中的call_interface和def_interface,在底层模块定义好相关的函数之后,交给上层模块去实现。
如在axhal中定义了TrapHandler如下:
#[def_interface]
pub trait TrapHandler {
/// Handles interrupt requests for the given IRQ number.
fn handle_irq(irq_num: usize);
// more e.g.: handle_page_fault();
// 需要分离用户态使用
#[cfg(feature = "monolithic")]
fn handle_syscall(syscall_id: usize, args: [usize; 6]) -> isize;
#[cfg(feature = "paging")]
fn handle_page_fault(addr: VirtAddr, flags: MappingFlags, tf: &mut TrapFrame);
#[cfg(feature = "paging")]
fn handle_access_fault(addr: VirtAddr, flags: MappingFlags);
/// 处理当前进程的信号
#[cfg(feature = "signal")]
fn handle_signal();
/// 为了lmbench特判,即在出现未能处理的情况,不panic,而是退出当前进程
#[cfg(feature = "monolithic")]
fn exit();
}
而在starry_libax/trap.rs完成了对TrapHandler的实现:
#[crate_interface::impl_interface]
impl axhal::trap::TrapHandler for TrapHandlerImpl {
fn handle_irq(irq_num: usize) {
/// ..
}
fn handle_syscall(syscall_id: usize, args: [usize; 6]) -> isize {
/// ..
}
#[cfg(feature = "paging")]
fn handle_page_fault(addr: VirtAddr, flags: MappingFlags, tf: &mut TrapFrame) {
/// ..
}
fn handle_access_fault(addr: VirtAddr, flags: MappingFlags) {
/// ..
}
#[cfg(feature = "signal")]
fn handle_signal() {
/// ..
}
fn exit() {
/// ..
}
}
而在axruntime/src/trap.rs中定义了ArceOS原有的unikernel架构下的trap实现:
/// 仅用作非宏内核下的trap入口
struct TrapHandlerImpl;
#[crate_interface::impl_interface]
impl axhal::trap::TrapHandler for TrapHandlerImpl {
fn handle_irq(_irq_num: usize) {
#[cfg(feature = "irq")]
{
let guard = kernel_guard::NoPreempt::new();
axhal::irq::dispatch_irq(_irq_num);
drop(guard); // rescheduling may occur when preemption is re-enabled.
}
}
}
通过不同的TrapHandler的实现,可以实现宏内核和unikernel架构下不同trap实现的支持。