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();
}

浙公网安备 33010602011771号