OS by Rust - 项目架构文档

2025-07-14

OS by Rust - 项目架构文档

项目概述

这是一个用 Rust 语言编写的操作系统内核,展示了现代系统编程的核心概念,特别是异步编程在操作系统中的应用。

项目地址

核心特性

  • 内存管理: 分页机制、物理内存分配、堆内存管理
  • 中断处理: 键盘中断、定时器中断、异常处理
  • 异步任务系统: 基于 Future 的协作式多任务
  • 设备驱动: VGA 文本输出、串口通信、键盘输入
  • 安全性: 利用 Rust 的类型系统保证内存安全

项目结构

os_by_rust_after_async_await/
├── src/
│   ├── main.rs              # 内核入口点
│   ├── lib.rs               # 库根模块
│   ├── allocator/           # 堆内存分配器
│   │   ├── mod.rs
│   │   ├── bump.rs          # 简单的bump分配器
│   │   ├── linked_list.rs   # 链表分配器
│   │   └── fixed_size_block.rs # 固定大小块分配器
│   ├── task/                # 异步任务系统
│   │   ├── mod.rs           # 任务抽象和ID生成
│   │   ├── executor.rs      # 高效的任务执行器
│   │   ├── simple_executor.rs # 简单的轮询执行器
│   │   └── keyboard.rs      # 键盘输入异步处理
│   ├── memory.rs            # 内存管理
│   ├── interrupts.rs        # 中断处理
│   ├── gdt.rs               # 全局描述符表
│   ├── vga_buffer.rs        # VGA文本输出
│   └── serial.rs            # 串口通信
├── tests/                   # 集成测试
├── async_test/              # 异步功能独立测试
└── Cargo.toml               # 项目配置

系统架构图

整体架构

graph TB
    subgraph "应用层"
        A[异步任务] --> B[Future]
        B --> C[Task]
    end

    subgraph "内核层"
        C --> D[Executor]
        D --> E[TaskWaker]
        E --> F[任务队列]

        G[键盘中断] --> H[中断处理器]
        H --> I[扫描码队列]
        I --> J[ScancodeStream]
        J --> K[键盘任务]
        K --> D

        L[定时器中断] --> M[定时器处理器]
        M --> N[调度器唤醒]
        N --> D
    end

    subgraph "硬件抽象层"
        O[键盘硬件] --> G
        P[定时器硬件] --> L
        Q[内存硬件] --> R[内存管理器]
        R --> S[页表]
        S --> T[堆分配器]
        T --> D
    end

    subgraph "硬件层"
        U[CPU] --> V[中断控制器]
        V --> G
        V --> L
        W[RAM] --> Q
        X[键盘控制器] --> O
        Y[定时器芯片] --> P
    end

内存管理架构

graph TD
    A[应用程序] --> B[alloc::Box/Vec]
    B --> C[GlobalAlloc trait]
    C --> D[FixedSizeBlockAllocator]
    D --> E[LinkedListAllocator fallback]
    E --> F[Locked wrapper]
    F --> G[堆内存区域]
    G --> H[页面分配器]
    H --> I[BootInfoFrameAllocator]
    I --> J[物理内存帧]

    K[虚拟地址] --> L[页表查找]
    L --> M[物理地址]
    M --> J

异步任务调度流程

graph LR
    A[创建Task] --> B[spawn到Executor]
    B --> C[加入task_queue]
    C --> D[run_ready_tasks]
    D --> E[poll任务]
    E --> F{返回值}
    F -->|Ready| G[任务完成]
    F -->|Pending| H[注册Waker]
    H --> I[等待事件]
    I --> J[事件发生]
    J --> K[Waker.wake]
    K --> L[任务重新入队]
    L --> D
    G --> M[清理资源]

核心模块详解

1. 异步任务系统 (src/task/)

设计理念

采用协作式多任务模型,基于 Rust 的 Future trait 实现异步编程。

核心组件

Task (任务抽象)

pub struct Task {
    pub id: TaskId,
    future: Pin<Box<dyn Future<Output = ()>>>,
}

Executor (执行器)

  • 维护任务队列和唤醒缓存
  • 实现高效的任务调度
  • 支持 CPU 休眠以节省电力

TaskWaker (唤醒器)

  • 实现 Wake trait
  • 将任务 ID 推入队列进行重新调度
  • 避免不必要的轮询

执行流程

  1. 任务被 spawn 到执行器
  2. 执行器轮询就绪任务
  3. 任务返回 Pending 时注册 waker
  4. 中断或事件触发时唤醒相关任务
  5. 任务重新进入就绪队列

2. 内存管理 (src/memory.rs, src/allocator/)

分层架构

应用层分配 (Box, Vec, etc.)
        ↓
全局分配器 (GlobalAlloc)
        ↓
堆分配器 (FixedSizeBlockAllocator)
        ↓
页面分配器 (BootInfoFrameAllocator)
        ↓
物理内存

分配策略

  • 固定大小块分配器: 用于小对象分配,减少碎片
  • 链表分配器: 用于大对象分配
  • Bump 分配器: 简单的线性分配器(仅用于测试)

3. 中断处理 (src/interrupts.rs)

中断向量表

lazy_static! {
    static ref IDT: InterruptDescriptorTable = {
        let mut idt = InterruptDescriptorTable::new();
        idt.breakpoint.set_handler_fn(breakpoint_handler);
        idt.double_fault.set_handler_fn(double_fault_handler);
        idt[InterruptIndex::Timer.as_usize()]
            .set_handler_fn(timer_interrupt_handler);
        idt[InterruptIndex::Keyboard.as_usize()]
            .set_handler_fn(keyboard_interrupt_handler);
        idt.page_fault.set_handler_fn(page_fault_handler);
        idt
    };
}

键盘中断处理流程

  1. 硬件产生键盘中断
  2. CPU 调用键盘中断处理器
  3. 读取扫描码并推入队列
  4. 唤醒键盘任务
  5. 异步处理键盘输入

运行流程

启动序列

sequenceDiagram
    participant Boot as Bootloader
    participant Kernel as kernel_main
    participant Init as 初始化
    participant Exec as Executor
    participant Task as 异步任务

    Boot->>Kernel: 传递BootInfo
    Kernel->>Init: 初始化GDT/IDT
    Init->>Init: 启用中断
    Kernel->>Init: 初始化内存管理
    Init->>Init: 设置堆分配器
    Kernel->>Exec: 创建执行器
    Kernel->>Task: 创建异步任务
    Exec->>Task: 开始执行循环
    Task->>Task: 处理键盘输入

异步任务执行流程

flowchart TD
    A[任务创建] --> B[加入执行器]
    B --> C[执行器轮询]
    C --> D{任务状态}
    D -->|Ready| E[执行任务]
    D -->|Pending| F[注册Waker]
    E --> G{任务完成?}
    G -->|是| H[移除任务]
    G -->|否| F
    F --> I[等待唤醒]
    I --> J[中断/事件]
    J --> K[唤醒任务]
    K --> C

技术特点与创新

1. 零成本抽象

  • 编译时优化: Rust 编译器在编译时进行大量优化,消除抽象层的运行时开销
  • 内联展开: 小函数和闭包在编译时被内联,避免函数调用开销
  • 单态化: 泛型代码为每个具体类型生成专门的代码,提高性能
  • RAII: 资源获取即初始化,自动管理资源生命周期

2. 异步编程模型

  • 协作式多任务: 任务主动让出控制权,避免抢占式调度的开销
  • 状态机转换: async/await 语法糖编译为高效的状态机
  • 零分配: Future 在栈上分配,避免堆分配开销
  • 组合性: 可以轻松组合多个异步操作

3. 内存安全保证

  • 所有权系统: 编译时跟踪内存所有权,防止 use-after-free
  • 借用检查: 确保引用的有效性,防止悬垂指针
  • 生命周期: 编译时验证引用的生命周期,确保内存安全
  • 类型安全: 强类型系统防止类型混淆和缓冲区溢出

4. 并发安全

  • Send/Sync trait: 编译时检查类型的线程安全性
  • 原子操作: 使用原子类型进行无锁编程
  • 通道通信: 通过消息传递而非共享内存进行通信
  • 锁自动释放: RAII 确保锁的正确释放

5. 性能优化技术

CPU 缓存友好设计

graph LR
    A[任务队列] --> B[连续内存布局]
    B --> C[缓存行对齐]
    C --> D[减少缓存未命中]

    E[BTreeMap] --> F[局部性原理]
    F --> G[预取优化]
    G --> H[提高访问速度]

内存分配策略

graph TD
    A["小对象 < 2KB"] --> B[FixedSizeBlockAllocator]
    B --> C[预分配块]
    C --> D[O1分配/释放]

    E["大对象 >= 2KB"] --> F[LinkedListAllocator]
    F --> G[首次适应算法]
    G --> H[减少碎片]

    I[频繁分配] --> J[对象池]
    J --> K[重用对象]
    K --> L[减少分配开销]

中断处理优化

  • 最小化中断禁用时间: 只在关键区域禁用中断
  • 中断处理分离: 将耗时操作推迟到任务中执行
  • 批量处理: 一次处理多个中断事件
  • 优先级调度: 高优先级中断优先处理

关键技术深度解析

异步状态机原理

当编译器遇到 async 函数时,会将其转换为实现 Future trait 的状态机:

// 原始async函数
async fn example_task() {
    let num = async_number().await;
    println!("async number is: {}", num);
}

// 编译器生成的状态机(简化版)
enum ExampleTaskState {
    Start,
    WaitingForNumber { future: AsyncNumberFuture },
    Done,
}

impl Future for ExampleTask {
    type Output = ();

    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> {
        loop {
            match self.state {
                Start => {
                    self.state = WaitingForNumber {
                        future: async_number()
                    };
                }
                WaitingForNumber { ref mut future } => {
                    match Pin::new(future).poll(cx) {
                        Poll::Ready(num) => {
                            println!("async number is: {}", num);
                            self.state = Done;
                        }
                        Poll::Pending => return Poll::Pending,
                    }
                }
                Done => return Poll::Ready(()),
            }
        }
    }
}

唤醒机制详解

sequenceDiagram
    participant Task as 异步任务
    participant Waker as TaskWaker
    participant Queue as 任务队列
    participant Interrupt as 中断处理器
    participant Executor as 执行器

    Task->>Executor: poll()
    Executor->>Task: 返回Pending
    Task->>Waker: 注册waker
    Waker->>Queue: 等待唤醒

    Note over Interrupt: 键盘中断发生
    Interrupt->>Queue: 添加扫描码
    Interrupt->>Waker: wake()
    Waker->>Queue: 推入task_id

    Executor->>Queue: 检查队列
    Queue->>Executor: 返回task_id
    Executor->>Task: 重新poll()
    Task->>Executor: 返回Ready

潜在问题与解决方案

1. 栈溢出问题

问题: 深度递归可能导致栈溢出 解决方案:

  • 使用独立的中断栈 (IST)
  • 实现栈保护机制
  • 限制递归深度

2. 中断延迟

问题: 长时间运行的任务可能延迟中断处理 解决方案:

  • 实现抢占式调度
  • 设置任务执行时间限制
  • 优化中断处理器性能

3. 内存碎片

问题: 频繁分配/释放导致内存碎片 解决方案:

  • 使用多种分配策略
  • 实现内存压缩
  • 采用对象池模式

4. 死锁风险

问题: 多个锁的获取顺序可能导致死锁 解决方案:

  • 统一锁获取顺序
  • 使用无锁数据结构
  • 实现死锁检测

性能优化

1. 任务调度优化

  • 使用优先级队列
  • 实现工作窃取算法
  • 减少上下文切换

2. 内存管理优化

  • 预分配常用大小的内存块
  • 实现内存池
  • 使用 NUMA 感知分配

3. 中断处理优化

  • 最小化中断处理时间
  • 使用中断线程化
  • 实现中断合并

扩展方向

1. 网络栈

  • 实现 TCP/IP 协议栈
  • 支持异步网络 I/O
  • 添加网络设备驱动

2. 文件系统

  • 实现基本文件系统
  • 支持异步文件操作
  • 添加存储设备驱动

3. 进程管理

  • 实现用户态进程
  • 支持进程间通信
  • 添加系统调用接口

4. 图形界面

  • 实现基本 GUI 框架
  • 支持窗口管理
  • 添加图形设备驱动

测试策略

1. 单元测试

  • 测试各个模块的核心功能
  • 验证边界条件处理
  • 确保错误处理正确

2. 集成测试

  • 测试模块间交互
  • 验证系统整体行为
  • 测试异常情况处理

3. 性能测试

  • 测量任务调度延迟
  • 评估内存分配性能
  • 分析中断处理时间

总结

这个项目成功展示了如何使用 Rust 构建一个现代化的操作系统内核,特别是异步编程在系统级编程中的应用。通过合理的架构设计和 Rust 的类型系统,我们实现了一个安全、高效、可扩展的操作系统基础框架。

项目的核心价值在于:

  1. 教育意义: 展示现代操作系统的核心概念
  2. 技术创新: 将异步编程引入操作系统内核
  3. 安全性: 利用 Rust 的类型系统保证内存安全
  4. 可扩展性: 模块化设计便于功能扩展