DragonOS Workqueue 机制设计文档

1. 概述

工作队列(Workqueue)是 DragonOS 内核中的一种”下半部”(Bottom Half)机制,用于将任务推迟到进程上下文中执行。与 Softirq 和 Tasklet 不同,工作队列的处理函数运行在内核线程中,因此允许执行以下操作:

  • 睡眠(阻塞等待资源)

  • 获取互斥锁(Mutex)或信号量

  • 执行 I/O 操作

  • 分配内存(可能导致阻塞)

本机制的设计参考了 Linux 内核的工作队列实现,并针对 DragonOS 的 Rust 架构进行了适配。

2. 核心架构

2.1 核心组件

Workqueue 机制主要由以下三个组件构成:

  1. Work(工作项)

    • 定义了需要延迟执行的具体任务。

    • 在 Rust 实现中,本质上是一个封装了闭包(Closure)或函数指针的结构体。

    • 通过 Arc 进行引用计数管理,支持跨线程共享。

  2. WorkQueue(工作队列)

    • 负责管理待处理的工作项(Pending Works)。

    • 维护一个或多个关联的工作线程(Worker Thread)

    • 内部包含一个 FIFO 队列和一个 WaitQueue(用于线程同步)。

  3. Worker Thread(工作线程)

    • 一个专门的内核线程,循环从 WorkQueue 中取出任务并执行。

    • 当队列为空时,线程进入睡眠状态(通过 WaitQueue 挂起)。

    • 当有新任务入队时,线程被唤醒。

2.2 数据流图

graph TD
    User[调用者 (Driver/Interrupt)] -->|schedule_work()| WQ[WorkQueue]
    WQ -->|enqueue| Queue[待处理队列]
    WQ -->|wakeup| Worker[Worker Thread]
    
    subgraph Worker Thread Loop
        Check{队列为空?} -->|Yes| Sleep[睡眠等待]
        Check -->|No| Dequeue[取出 Work]
        Dequeue --> Run[执行 Work.func()]
        Run --> Check
    end
    
    Sleep -.->|被唤醒| Check

3. 详细设计

3.1 数据结构设计

Work

Work 结构体封装了具体的任务逻辑。使用 Box<dyn Fn() + Send + Sync> 来存储闭包,确保其可以在线程间安全传递和执行。

pub struct Work {
    func: Box<dyn Fn() + Send + Sync>,
}

WorkQueue

WorkQueue 是核心管理结构。它包含:

  • queue: 一个受自旋锁(SpinLock)保护的双端队列,存储 Arc<Work>

  • wait_queue: 用于工作线程的同步等待。

  • worker: 指向关联内核线程 PCB 的引用。

pub struct WorkQueue {
    queue: SpinLock<VecDeque<Arc<Work>>>,
    wait_queue: Arc<WaitQueue>,
    worker: SpinLock<Option<Arc<ProcessControlBlock>>>,
}

3.2 运行机制

  1. 初始化

    • 调用 WorkQueue::new(name) 创建一个新的工作队列。

    • 该操作会自动启动一个名为 name 的内核线程。

  2. 任务调度

    • 调用者使用 Work::new(closure) 创建工作项。

    • 调用 wq.enqueue(work) 将任务加入队列。

    • enqueue 操作会将任务推入队列尾部,并调用 wait_queue.wakeup() 唤醒工作线程。

  3. 任务执行

    • 工作线程运行 worker_loop 函数。

    • 循环检查队列状态:

      • 若队列非空:取出队首任务,调用 work.run() 执行。

      • 若队列为空:在 wait_queue 上调用 wait_event_interruptible 进入睡眠,等待被唤醒。

3.3 系统全局队列 (SYSTEM_WQ)

为了简化常见场景的使用,系统提供了一个全局默认工作队列 SYSTEM_WQ

  • 大多数简单的后台任务可以直接提交到该队列,无需创建专用的 WorkQueue。

  • 通过 schedule_work(work) 接口直接调度。

4. 接口说明

4.1 创建任务

let work = Work::new(|| {
    log::info!("This is running in a workqueue!");
    // 可以安全地睡眠或加锁
});

4.2 调度任务

// 调度到系统默认队列
schedule_work(work);

// 或者调度到自定义队列
let my_wq = WorkQueue::new("my_driver_wq");
my_wq.enqueue(work);

5. 与 Linux 的异同

  • 相同点

    • 都是基于内核线程的延迟执行机制。

    • 都支持睡眠和阻塞操作。

    • 都提供了系统默认的全局队列。

  • 不同点

    • 并发模型:DragonOS 当前实现采用”单线程单队列”模型(每个 WorkQueue 对应一个内核线程)。Linux 采用复杂的 Worker Pool 模型,支持动态扩缩容和并发管理(CM)。

    • Per-CPU:DragonOS 当前未实现 Per-CPU 的 WorkQueue,所有任务在同一个线程队列中处理。

    • API 风格:DragonOS 利用 Rust 的闭包特性,使得任务定义更加灵活简洁,不需要像 Linux 那样嵌入 work_struct 到自定义结构体中(尽管也支持类似模式)。

6. 未来演进

  1. 并发管理:引入 Worker Pool 机制,允许多个 Worker 消费同一个队列,提高并发度。

  2. Per-CPU 队列:实现 Per-CPU 的 WorkQueue,减少锁竞争,提高缓存局部性。

  3. 延迟工作(Delayed Work):集成定时器机制,支持 schedule_delayed_work,允许任务在指定延迟后执行。