Skip to content

依赖问题

由于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的模块依赖图如下:

modules

如axhal需要定义trap入口,而trap实现需要很多模块的支持如axmem的地址空间等,此时就可能出现循环依赖的情况,即axhal依赖于axmem,而axmem依赖于axhal。

为了解决这个问题,有以下几种方法:

  1. 优化结构设计,即尽可能将实现的功能进行划分,如地址空间内容独立出来放在axmem,而不是和进程控制一起放在axprocess。

  2. 通过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实现的支持。