Rust-os-lab04:内核异步处理

一、实验完成情况

实验完成了所有的实验任务,包括任务管理模块(创建协程任务、Spawn任务、执行协程任务)、Waker唤醒器设计、键盘事件流协程、鼠标事件流协程、睡眠协程、命令解释器功能拓展。

实验结果展示

键盘事件流

鼠标事件流

睡眠协程

命令解释器功能拓展

二、主要代码介绍

任务管理模块实现

Waker唤醒器设计

键盘事件流协程

鼠标事件流协程

睡眠协程

(一)同步模式下的睡眠协程设计
同步模式下,我们在判断是执行同步的睡眠协程命令之后,进入loop循环,在执行到达相应的事件之后跳出循环,输出字符串执行指令。
首先我们应该设置好相应的全局变量,包括计数器TIMER_TICK和唤醒器TIMER_WAKER,每次时钟中断的时候改变TIMER_TICK,到达预期时间后调用TIMER_WAKE.wake(),这两者的变量类型分别是AtomicUsize和AtomicWaker,保证执行过程中的确定性;

/// 睡眠协程唤醒器
pub static TIMER_WAKER: AtomicWaker = AtomicWaker::new();
/// 睡眠计数器
pub static TIMER_TICK: AtomicUsize = AtomicUsize::new(0);

其次我们应该在命令解释器shell.rs中读入并解释命令之后,定义TIMER_TICK全局变量并设置初始值;

// 同步运行命令
TIMER_TICK.store(0, Ordering::Relaxed);
loop {
    if TIMER_TICK.load(Ordering::Relaxed) >= ticks {
        break;
    }
}
run_command(tokens[2..].to_vec());

我们在idt.rs中的时钟中断部分添加了对于两个全局变量TIMER_TICK和TIMER_WAKER的修改,我们设定的是每次时钟中断的时候,计数器都会加1,在初始设定的时候我们将计数器设置为0,当到达ticks的时候,循环就会中断,从而实现到达睡眠一定时间跳出的效果。
关于时钟中断的部分:

extern "x86-interrupt" fn timer_interrupt_handler(_stack_frame: InterruptStackFrame) {
    TIMER_TICK.fetch_add(1, Ordering::Relaxed);
    TIMER_WAKER.wake();
    notify_eoi(InterruptIndex::Timer as u8);
}

(二)异步模式下的睡眠协程设计
在异步模式下,我们编写了SleepFuture,实现了其Future Trait,通过Future来实现异步的睡眠协程,等待时间不占有CPU,鼠标键盘均可正常使用。
我们应当设计的部分就是SleepFuture,定义的结构体中的实现只有一个参数limit,表示的就是该协程需要判断的sleep需要执行多少个时钟周期。

struct SleepFuture {
    limits: usize,
}

具体的时间计数器判断与同步模式下的睡眠协程设计相同,都是加时钟计数器的方式,到达相应的边界条件之后进行判断并输出。

impl SleepFuture {
    fn new(t_sum: usize) -> Self {
        let limits = TIMER_TICK.load(Ordering::Relaxed) + t_sum;
        SleepFuture { limits }
    }
}

impl Future for SleepFuture {
    type Output = ();

    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
        if TIMER_TICK.load(Ordering::Relaxed) >= self.limits {
            Poll::Ready(())
        } else {
            TIMER_WAKER.register(&cx.waker());
            Poll::Pending
        }
    }
}

最后关于如何调用这个Future类型,第一步是在命令解释器中判断是异步运行的情况下,我们在协程中调用sleep.rs中的函数sleep

if env.get("MODE").is_some_and(|value| value == "async") {
    // 异步运行命令
    let _ = executor::SPAWNED_TASKS
        .lock()
        .push(Task::new(sleep::sleep(ticks, move || {
            run_command(tokens[2..].to_vec())
        })));
}

命令解释器功能拓展

命令解释器的功能拓展主要包含的内容方面是关于sleep/env/export命令的实现,前者主要是与第三问相关的sleep的具体实现,后两者主要的内容是环境变量的查询env与增添修改export
我们要更新两个部分:关于help命令的输入部分,关于具体命令的解释部分。
注意对于环境变量的命令来说,我们需要在shell.rs中添加环境变量:

use alloc::collections::BTreeMap;
static ENV: Lazy<Mutex<BTreeMap<String, String>>> = Lazy::new(|| Mutex::new(BTreeMap::new()));

还需要引入一些库,用于实现具体的调用:

use crate::task::sleep::TIMER_TICK;
use crate::task::{executor, sleep, Task};
use core::str::FromStr;
use core::sync::atomic::Ordering;

之后就是命令解释器的执行部分:

"env" => {
    for iter in ENV.lock().clone().into_iter() {
        //fb_println!("{}={}", iter.0, iter.1);
        fb_println!("  {:x?}", iter);
    }
}
"export" => {
    let s = tokens[1].split("=").collect::<Vec<_>>().to_vec();
    ENV.lock().insert(String::from(s[0]), String::from(s[1]));
}
"sleep" => {
    // 新加sleep
    if tokens.len() < 2 {
        fb_println!("Error: expect a sleep ticks, example usage: sleep 10 echo hello");
        return;
    }
    let ticks = match usize::from_str(tokens[1].as_str()) {
        Ok(ticks) => ticks,
        Err(_) => {
            fb_println!("Error: can not convert to number: {}", tokens[1]);
            return;
        }
    };
    if tokens.len() < 3 {
        fb_println!("Error: expect a target command after sleeping, example usage: sleep 10 echo hello");
        return;
    }

    let env = ENV.lock();
    if env.get("MODE").is_some_and(|value| value == "async") {
        // 异步运行命令
        let _ = executor::SPAWNED_TASKS
            .lock()
            .push(Task::new(sleep::sleep(ticks, move || {
                run_command(tokens[2..].to_vec())
            })));
    } else {
        // 同步运行命令
        TIMER_TICK.store(0, Ordering::Relaxed);
        loop {
            if TIMER_TICK.load(Ordering::Relaxed) >= ticks {
                break;
            }
        }
        run_command(tokens[2..].to_vec());
    }
}

同时对于help.rs也要进行相应的修改。

"help" => {
    fb_println!("Try commands:");
    fb_println!("\thelp");
    fb_println!("\tclear");
    fb_println!("\techo hi");
    fb_println!("\treboot");
    fb_println!("\tenv");
    fb_println!("\texport");
    fb_println!("\tsleep");
    fb_println!("\texit (qemu only)");
    fb_println!("");
}

附录(源码等)

keyboard.rs:

//! 键盘事件流

use core::{
    pin::Pin,
    task::{Context, Poll},
};
use crossbeam_queue::ArrayQueue;
use futures_util::stream::Stream;
use futures_util::stream::StreamExt;
use futures_util::task::AtomicWaker;
use pc_keyboard::{layouts, DecodedKey, HandleControl, Keyboard, ScancodeSet1};
/// 键盘协程唤醒器
static KEYBOARD_WAKER: AtomicWaker = AtomicWaker::new();
use spin::Once;

/// 键盘事件队列
static SCANCODE_QUEUE: Once<ArrayQueue<u8>> = Once::new();

/// 添加键盘事件
pub fn add_scancode(scancode: u8) {
    if let Some(queue) = SCANCODE_QUEUE.get() {
        if let Err(_) = queue.push(scancode) {
            serial_println!("WARNING: scancode queue full; dropping keyboard input");
        } else {
            KEYBOARD_WAKER.wake(); //更新部分
        }
    } else {
        serial_println!("WARNING: scancode queue uninitialized");
    }
}

/// 异步事件流
pub struct ScancodeStream;

impl ScancodeStream {
    /// 创建键盘事件流
    pub fn new() -> Self {
        SCANCODE_QUEUE.call_once(|| ArrayQueue::new(100));
        ScancodeStream
    }
}

impl Stream for ScancodeStream {
    type Item = u8;
    fn poll_next(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Option<u8>> {
        let queue = SCANCODE_QUEUE
            .get()
            .expect("scancode queue not initialized");

        //更新部分:增加判断快速通道
        if let Some(scancode) = queue.pop() {
            return Poll::Ready(Some(scancode));
        }

        //更新部分
        KEYBOARD_WAKER.register(&cx.waker());
        match queue.pop() {
            Some(scancode) => {
                KEYBOARD_WAKER.take();
                Poll::Ready(Some(scancode))
            }
            None => Poll::Pending,
        }
    }
}

/// 异步任务
pub async fn print_keypresses() {
    let mut scancodes = ScancodeStream::new();
    let mut keyboard = Keyboard::new(
        ScancodeSet1::new(),
        layouts::Us104Key,
        HandleControl::Ignore,
    );

    while let Some(scancode) = scancodes.next().await {
        if let Ok(Some(key_event)) = keyboard.add_byte(scancode) {
            if let Some(key) = keyboard.process_keyevent(key_event) {
                match key {
                    DecodedKey::Unicode(character) => {
                        //fb_print!("{}", character);
                        crate::shell::shell_input(character);
                    }
                    DecodedKey::RawKey(_key) => {
                        //fb_print!("{:?}", key);
                    }
                }
            }
        }
    }
}

mouse.rs

//! 鼠标流事件

use core::pin::Pin;
/// 鼠标协程唤醒器
use core::task::{Context, Poll};
use crossbeam_queue::ArrayQueue;
use futures_util::stream::Stream;
use futures_util::stream::StreamExt;
use futures_util::task::AtomicWaker;
use spin::Once;
static MOUSE_WAKER: AtomicWaker = AtomicWaker::new();
/// 鼠标事件队列
static MOUSE_EVENT_QUEUE: Once<ArrayQueue<u8>> = Once::new();

/// 添加鼠标事件
pub fn add_mouse_event(package: u8) {
    if let Some(queue) = MOUSE_EVENT_QUEUE.get() {
        if let Err(_) = queue.push(package) {
            serial_println!("WARNING: mouse event queue full!");
        } else {
            MOUSE_WAKER.wake();
        }
    } else {
        serial_println!("WARNING: mouse event queue uninitialized!");
    }
}

/// 异步事件流
pub struct MouseEventStream;

impl MouseEventStream {
    /// 创建鼠标事件流
    pub fn new() -> Self {
        MOUSE_EVENT_QUEUE.call_once(|| ArrayQueue::new(100));
        MouseEventStream
    }
}

impl Stream for MouseEventStream {
    type Item = u8;
    fn poll_next(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Option<u8>> {
        let queue = MOUSE_EVENT_QUEUE
            .get()
            .expect("mouse event queue not initialized");

        // 检查队列是否有鼠标事件
        if let Some(package) = queue.pop() {
            return Poll::Ready(Some(package));
        }

        MOUSE_WAKER.register(&cx.waker());
        match queue.pop() {
            Some(package) => {
                MOUSE_WAKER.take();
                Poll::Ready(Some(package))
            }
            None => Poll::Pending,
        }
    }
}

/// 实现异步鼠标任务
pub async fn print_mousemovements() {
    let mut mouses = MouseEventStream::new();
    while let Some(mouse_event) = mouses.next().await {
        crate::driver::mouse::mouse_event(mouse_event);
    }
}

sleep.rs

//! 异步睡眠

use core::sync::atomic::{AtomicUsize, Ordering};
use core::{
    future::Future,
    pin::Pin,
    task::{Context, Poll},
};

use futures_util::task::AtomicWaker;

/// 睡眠协程唤醒器
pub static TIMER_WAKER: AtomicWaker = AtomicWaker::new();
/// 睡眠计数器
pub static TIMER_TICK: AtomicUsize = AtomicUsize::new(0);

struct SleepFuture {
    limits: usize,
}

impl SleepFuture {
    fn new(t_sum: usize) -> Self {
        let limits = TIMER_TICK.load(Ordering::Relaxed) + t_sum;
        SleepFuture { limits }
    }
}

impl Future for SleepFuture {
    type Output = ();

    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
        if TIMER_TICK.load(Ordering::Relaxed) >= self.limits {
            Poll::Ready(())
        } else {
            TIMER_WAKER.register(&cx.waker());
            Poll::Pending
        }
    }
}

/// 异步睡眠函数
pub async fn sleep(t_sum: usize, command: impl FnOnce() + Send + 'static) {
    let sleep_future = SleepFuture::new(t_sum);
    sleep_future.await;
    command();
    fb_print!("[rust_os] >>> ");
}

idt.rs:

//! 中断描述符表模块
use core::sync::atomic::Ordering;
// use crate::driver::mouse::mouse_event;
use crate::driver::pic::{notify_eoi, InterruptIndex};
use crate::driver::serial::receive;
use crate::task::sleep::{TIMER_TICK, TIMER_WAKER};
// use pc_keyboard::{layouts, DecodedKey, HandleControl, Keyboard, ScancodeSet1};
use spin::Lazy;
use x86_64::structures::idt::{InterruptDescriptorTable, InterruptStackFrame};

static IDT: Lazy<InterruptDescriptorTable> = Lazy::new(|| {
    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[InterruptIndex::Com1 as usize].set_handler_fn(com1_interrupt_handler);
    idt[InterruptIndex::Mouse as usize].set_handler_fn(mouse_interrupt_handler);
    idt
});

/// 初始化中断描述符表
pub fn init() {
    IDT.load();
}

extern "x86-interrupt" fn breakpoint_handler(stack_frame: InterruptStackFrame) {
    fb_println!("EXCEPTION: BREAKPOINT\n{:#?}", stack_frame);
}

extern "x86-interrupt" fn double_fault_handler(
    stack_frame: InterruptStackFrame,
    _error_code: u64,
) -> ! {
    panic!("EXCEPTION: DOUBLE FAULT\n{:#?}", stack_frame);
}

extern "x86-interrupt" fn timer_interrupt_handler(_stack_frame: InterruptStackFrame) {
    // crate::task::sleep::add_sleep_time();
    // fb_println!("  $${}", TIMER_TICK.load(Relaxed));
    TIMER_TICK.fetch_add(1, Ordering::Relaxed);
    TIMER_WAKER.wake();
    notify_eoi(InterruptIndex::Timer as u8);
}

/*
extern "x86-interrupt" fn keyboard_interrupt_handler(_stack_frame: InterruptStackFrame) {
    use x86_64::instructions::port::Port;
    let mut port = Port::new(0x60);
    let scancode: u8 = unsafe { port.read() };
    let mut keyboard = Keyboard::new(
        ScancodeSet1::new(),
        layouts::Us104Key,
        HandleControl::Ignore,
    );

    if let Ok(Some(key_event)) = keyboard.add_byte(scancode) {
        if let Some(key) = keyboard.process_keyevent(key_event) {
            match key {
                DecodedKey::Unicode(character) => {
                    crate::shell::shell_input(character);
                }
                DecodedKey::RawKey(_key) => {
                    fb_print!("{:?}", key);
                }
            }
        }
    }
    notify_eoi(InterruptIndex::Keyboard as u8);
}
*/

extern "x86-interrupt" fn keyboard_interrupt_handler(_stack_frame: InterruptStackFrame) {
    use x86_64::instructions::port::Port;
    let mut port = Port::new(0x60);
    let scancode: u8 = unsafe { port.read() };
    crate::task::keyboard::add_scancode(scancode); //更新部分
    notify_eoi(InterruptIndex::Keyboard as u8);
}

extern "x86-interrupt" fn com1_interrupt_handler(_stack_frame: InterruptStackFrame) {
    let chr = receive();
    serial_print!("{}", chr as char);
    notify_eoi(InterruptIndex::Com1 as u8);
}

extern "x86-interrupt" fn mouse_interrupt_handler(_stack_frame: InterruptStackFrame) {
    use x86_64::instructions::port::Port;
    let mut port = Port::new(0x60);
    let package: u8 = unsafe { port.read() };
    crate::task::mouse::add_mouse_event(package);
    // mouse_event(package);
    notify_eoi(InterruptIndex::Mouse as u8);
}

#[test_case]
fn test_breakpoint() {
    x86_64::instructions::interrupts::int3();
}

shell.rs

//! 实现简单的shell

use crate::driver::fb::{clear, delete};
use crate::task::sleep::TIMER_TICK;
use crate::task::{executor, sleep, Task};
use crate::test::{exit_qemu, QemuExitCode};
use alloc::collections::BTreeMap;
use alloc::string::{String, ToString};
use alloc::vec::Vec;
use core::arch::asm;
use core::str::FromStr;
use core::sync::atomic::Ordering;
use spin::{Lazy, Mutex};

/// sleep环境变量
static ENV: Lazy<Mutex<BTreeMap<String, String>>> = Lazy::new(|| Mutex::new(BTreeMap::new()));

/// 命令行缓冲
static CMD_BUF: Lazy<Mutex<String>> = Lazy::new(|| Mutex::new(String::new()));

/// 处理键盘输入
pub fn shell_input(chr: char) {
    let mut command = CMD_BUF.lock();

    match chr as u8 {
        10 => {
            // 处理换行
            fb_print!("\n");

            // 命令缓冲非空,执行命令
            let cmd = command.trim();
            if cmd.len() != 0 {
                let tokens: Vec<String> =
                    cmd.split_whitespace().map(|str| str.to_string()).collect();
                run_command(tokens);
            }
            command.clear();
            fb_print!("[rust_os] >>> ");
        }
        8 => {
            // 处理删除
            if command.len() != 0 {
                command.pop();
                // 退格、打印空格、再退格
                delete();
                fb_print!(" ");
                delete();
            }
        }
        other => {
            // 处理其它字符, 加入命令缓冲区并输出
            command.push(other as char);
            fb_print!("{}", other as char);
        }
    };
}

/// 命令解释器执行命令
pub fn run_command(tokens: Vec<String>) {
    // 解析命令
    match tokens[0].as_str() {
        "clear" => {
            clear();
        }
        "echo" => {
            if tokens.len() < 2 {
                fb_println!("Error: expect a string to echo, example usage: echo hello");
                return;
            }
            fb_println!("{}", tokens[1]);
        }
        "exit" => {
            exit_qemu(QemuExitCode::Success);
        }
        "help" => {
            fb_println!("Try commands:");
            fb_println!("\thelp");
            fb_println!("\tclear");
            fb_println!("\techo hi");
            fb_println!("\treboot");
            fb_println!("\tenv");
            fb_println!("\texport");
            fb_println!("\tsleep");
            fb_println!("\texit (qemu only)");
            fb_println!("");
        }
        "reboot" => unsafe {
            asm!(
                "cli
                mov al, 0xfe
                out 0x64, al",
                options(nomem, nostack, preserves_flags)
            );
        },
        "env" => {
            for iter in ENV.lock().clone().into_iter() {
                //fb_println!("{}={}", iter.0, iter.1);
                fb_println!("  {:x?}", iter);
            }
        }
        "export" => {
            let s = tokens[1].split("=").collect::<Vec<_>>().to_vec();
            ENV.lock().insert(String::from(s[0]), String::from(s[1]));
        }
        "sleep" => {
            // 新加sleep
            if tokens.len() < 2 {
                fb_println!("Error: expect a sleep ticks, example usage: sleep 10 echo hello");
                return;
            }
            let ticks = match usize::from_str(tokens[1].as_str()) {
                Ok(ticks) => ticks,
                Err(_) => {
                    fb_println!("Error: can not convert to number: {}", tokens[1]);
                    return;
                }
            };
            if tokens.len() < 3 {
                fb_println!("Error: expect a target command after sleeping, example usage: sleep 10 echo hello");
                return;
            }

            let env = ENV.lock();
            if env.get("MODE").is_some_and(|value| value == "async") {
                // 异步运行命令
                let _ = executor::SPAWNED_TASKS
                    .lock()
                    .push(Task::new(sleep::sleep(ticks, move || {
                        run_command(tokens[2..].to_vec())
                    })));
            } else {
                // 同步运行命令
                TIMER_TICK.store(0, Ordering::Relaxed);
                loop {
                    if TIMER_TICK.load(Ordering::Relaxed) >= ticks {
                        break;
                    }
                }
                run_command(tokens[2..].to_vec());
            }
        }
        _ => {
            fb_println!("Error: unexpected command");
        }
    };
}

main.rs

#![no_std]
#![no_main]
#![warn(missing_docs)]
#![warn(warnings)]

//! 内核主函数
use bootloader_api::{config::Mapping, BootInfo, BootloaderConfig};
use kernel::mem::KERNEL_PHY_OFFSET;
use kernel::task::{executor::Executor, Task};
/// bootloader配置
pub static BOOTLOADER_CONFIG: BootloaderConfig = {
    let mut config = BootloaderConfig::new_default();
    config.mappings.physical_memory = Some(Mapping::FixedAddress(KERNEL_PHY_OFFSET as _));
    config
};

// 使用bootloader_api库提供的宏声明内核入口
bootloader_api::entry_point!(kernel_main, config = &BOOTLOADER_CONFIG);

/// 内核入口函数,参数为bootloader收集的硬件信息
fn kernel_main(boot_info: &'static mut BootInfo) -> ! {
    kernel::init(boot_info);
    kernel::fb_println!("Use the \"help\" command to get usage information.");
    kernel::fb_print!("[rust_os] >>> ");

    // 添加异步程序并执行
    let mut executor = Executor::new();
    executor.spawn(Task::new(kernel::task::keyboard::print_keypresses())); //新增部分
    executor.spawn(Task::new(kernel::task::mouse::print_mousemovements())); // 鼠标操作
    executor.run();
}
posted @ 2023-07-06 09:53  6954717  阅读(16)  评论(0)    收藏  举报