系统调用表实现方案

备注

Author: longjin <longjin@dragonos.org>

Date: 2025/05/13

概述

        classDiagram
   class Syscall {
      <<trait>>
      +num_args() usize
      +handle(args, from_user) Result<usize, SystemError>
      +entry_format(args) Vec<FormattedSyscallParam>
   }

   class SyscallHandle {
      +nr: usize
      +inner_handle: &dyn Syscall
   }

   class SyscallTable {
      -entries: [Option<&SyscallHandle>; 512]
      +get(nr) Option<&dyn Syscall>
   }

   Syscall <|.. SysXXXXXXHandle
   SyscallHandle "1" *-- "1" Syscall
   SyscallTable "1" *-- "512" SyscallHandle
    

系统调用表架构

相比于将原本集中在一个大match中的系统调用分发,本方案采用基于trait和系统调用表的实现。主要优势包括:

  • 降低栈内存使用:避免单个大函数占用过多栈空间

  • 支持参数打印:通过统一的参数格式化接口

  • 更好的扩展性:新增系统调用无需修改分发逻辑

核心设计

Syscall Trait

所有系统调用处理函数都需要实现 Syscall trait:

pub trait Syscall: Send + Sync + 'static {
    fn num_args(&self) -> usize;
    fn handle(&self, args: &[usize], from_user: bool) -> Result<usize, SystemError>;
    fn entry_format(&self, args: &[usize]) -> Vec<FormattedSyscallParam>;
}
  • num_args(): 返回该系统调用需要的参数数量

  • handle(): 实际执行系统调用处理

  • entry_format(): 格式化参数用于调试打印

SyscallHandle

SyscallHandle 结构体将系统调用号与处理函数关联:

pub struct SyscallHandle {
    pub nr: usize,  // 系统调用号
    pub inner_handle: &'static dyn Syscall,  // 处理函数
    pub name: &'static str,
}

SyscallTable

SyscallTable 管理所有系统调用:

  • 固定大小512项

  • 编译时初始化

  • 通过系统调用号快速查找处理函数

使用方式

实现系统调用

  1. 定义实现``Syscall`` trait的结构体

  2. 实现``handle()``和``entry_format()``方法

  3. 使用``declare_syscall!``宏注册

参考实现:sys_write.rs

注册系统调用

使用``declare_syscall!``宏注册系统调用:

syscall_table_macros::declare_syscall!(SYS_WRITE, SysWriteHandle);

参数说明:

  1. 系统调用名称(用于生成符号)

  2. 实现``Syscall`` trait的结构体

初始化流程

  1. 内核启动时调用``syscall_table_init()``

  2. 从链接器符号``_syscall_table``加载所有注册的系统调用

  3. 填充系统调用表