diff --git a/docs/kernel/sched/waiting.md b/docs/kernel/sched/c_waiting.md similarity index 96% rename from docs/kernel/sched/waiting.md rename to docs/kernel/sched/c_waiting.md index bc4f84a5..f6f19291 100644 --- a/docs/kernel/sched/waiting.md +++ b/docs/kernel/sched/c_waiting.md @@ -1,4 +1,10 @@ -# 与“等待”相关的api +# 与“等待”相关的api(C语言) + +:::{warning} + +随着内核的发展,我们将会逐步将C语言的等待机制替换为Rust语言的等待机制。在这个过程中,我们将会同时保留C语言和Rust语言的等待机制,以便于我们在开发过程中进行对比。 +待时机成熟,我们将会逐步将C语言的等待机制移除。 +:::   如果几个进程需要等待某个事件发生,才能被运行,那么就需要一种“等待”的机制,以实现进程同步。 diff --git a/docs/kernel/sched/index.rst b/docs/kernel/sched/index.rst index 223e5245..3dd03232 100644 --- a/docs/kernel/sched/index.rst +++ b/docs/kernel/sched/index.rst @@ -7,4 +7,5 @@ DragonOS调度 .. toctree:: :maxdepth: 1 - waiting + c_waiting + rust_waiting diff --git a/docs/kernel/sched/rust_waiting.md b/docs/kernel/sched/rust_waiting.md new file mode 100644 index 00000000..1ce54511 --- /dev/null +++ b/docs/kernel/sched/rust_waiting.md @@ -0,0 +1,81 @@ +# 与“等待”相关的api(rust语言) + +  如果几个进程需要等待某个事件发生,才能被运行,那么就需要一种“等待”的机制,以实现进程同步。 + +## 1. WaitQueue等待队列 + +   WaitQueue是一种进程同步机制,中文名为“等待队列”。它可以将当前进程挂起,并在时机成熟时,由另一个进程唤醒他们。 + +  当您需要等待一个事件完成时,使用 WaitQueue机制能减少进程同步的开销。相比于滥用自旋锁以及信号量,或者是循环使用usleep(1000)这样的函数来完成同步, WaitQueue是一个高效的解决方案。 + +### 1.1 WaitQueue的使用 + +   WaitQueue的使用非常简单,只需要三步: + +1. 初始化一个WaitQueue对象。 +2. 调用这个WaitQueue的挂起相关的API,将当前进程挂起。 +3. 当事件发生时,由另一个进程,调用这个WaitQueue的唤醒相关的API,唤醒一个进程。 + +  下面是一个简单的例子: + +### 1.1.1 初始化一个WaitQueue对象 + +   WaitQueue对象的初始化非常简单,只需要调用WaitQueue::INIT即可。 + +```rust +let mut wq = WaitQueue::INIT; +``` + +### 1.1.2 挂起进程 + +   您可以这样挂起当前进程: + +```rust +wq.sleep(); +``` + +   当前进程会被挂起,直到有另一个进程调用了`wq.wakeup()`。 + +### 1.1.3 唤醒进程 + +   您可以这样唤醒一个进程: + +```rust +// 唤醒等待队列头部的进程(如果它的state & PROC_INTERRUPTIBLE 不为0) +wq.wakeup(PROC_INTERRUPTIBLE); + +// 唤醒等待队列头部的进程(如果它的state & PROC_UNINTERRUPTIBLE 不为0) +wq.wakeup(PROC_UNINTERRUPTIBLE); + +// 唤醒等待队列头部的进程(无论它的state是什么) +wq.wakeup((-1) as u64); +``` + +### 1.2 API + +### 1.2.1 挂起进程 + +  您可以使用以下函数,将当前进程挂起,并插入到指定的等待队列。这些函数大体功能相同,只是在一些细节上有所不同。 + +| 函数名 | 解释 | +| --------------------------------------- | ------------------------------------------------------------- | +| sleep() | 将当前进程挂起,并设置进程状态为PROC_INTERRUPTIBLE | +| sleep_uninterruptible() | 将当前进程挂起,并设置进程状态为PROC_UNINTERRUPTIBLE | +| sleep_unlock_spinlock() | 将当前进程挂起,并设置进程状态为PROC_INTERRUPTIBLE。待当前进程被插入等待队列后,解锁给定的自旋锁 | +| sleep_unlock_mutex() | 将当前进程挂起,并设置进程状态为PROC_INTERRUPTIBLE。待当前进程被插入等待队列后,解锁给定的Mutex | +| sleep_uninterruptible_unlock_spinlock() | 将当前进程挂起,并设置进程状态为PROC_UNINTERRUPTIBLE。待当前进程被插入等待队列后,解锁给定的自旋锁 | +| sleep_uninterruptible_unlock_mutex() | 将当前进程挂起,并设置进程状态为PROC_UNINTERRUPTIBLE。待当前进程被插入等待队列后,解锁给定的Mutex | + +### 1.2.2 唤醒进程 + +  您可以使用`wakeup(state)`函数,唤醒等待队列中的第一个进程。如果这个进程的state与给定的state进行and操作之后,结果不为0,则唤醒它。 + +  返回值:如果有进程被唤醒,则返回true,否则返回false。 + +### 1.2.3 其它API + +| 函数名 | 解释 | +| ----- | ------------ | +| len() | 返回等待队列中的进程数量 | + + diff --git a/kernel/src/lib.rs b/kernel/src/lib.rs index d2fe6200..0dc2aef2 100644 --- a/kernel/src/lib.rs +++ b/kernel/src/lib.rs @@ -5,7 +5,7 @@ #![feature(alloc_error_handler)] #![feature(panic_info_message)] #![feature(drain_filter)] // 允许Vec的drain_filter特性 -#![feature(c_void_variant)] //not stable, used in /home/su/Documents/VSCode/DragonOS/kernel/src/exception/softirq.rs +#![feature(c_void_variant)] // used in kernel/src/exception/softirq.rs #[allow(non_upper_case_globals)] #[allow(non_camel_case_types)] #[allow(non_snake_case)] diff --git a/kernel/src/libs/wait_queue.rs b/kernel/src/libs/wait_queue.rs index 2f308288..c1e6c339 100644 --- a/kernel/src/libs/wait_queue.rs +++ b/kernel/src/libs/wait_queue.rs @@ -1,6 +1,19 @@ -use crate::include::bindings::bindings::wait_queue_head_t; +#![allow(dead_code)] +use alloc::collections::LinkedList; -use super::list::list_init; +use crate::{ + arch::{asm::current::current_pcb, sched::sched}, + include::bindings::bindings::{ + process_control_block, process_wakeup, wait_queue_head_t, PROC_INTERRUPTIBLE, + PROC_UNINTERRUPTIBLE, + }, +}; + +use super::{ + list::list_init, + mutex::MutexGuard, + spinlock::{SpinLock, SpinLockGuard}, +}; impl Default for wait_queue_head_t { fn default() -> Self { @@ -12,3 +25,116 @@ impl Default for wait_queue_head_t { return x; } } + +#[derive(Debug)] +struct InnerWaitQueue { + /// 等待队列的链表 + wait_list: LinkedList<&'static mut process_control_block>, +} + +/// 被自旋锁保护的等待队列 +#[derive(Debug)] +pub struct WaitQueue(SpinLock); + +impl WaitQueue { + pub const INIT: WaitQueue = WaitQueue(SpinLock::new(InnerWaitQueue::INIT)); + + /// @brief 让当前进程在等待队列上进行等待,并且,允许被信号打断 + pub fn sleep(&self) { + let mut guard: SpinLockGuard = self.0.lock(); + current_pcb().state = PROC_INTERRUPTIBLE as u64; + guard.wait_list.push_back(current_pcb()); + drop(guard); + sched(); + } + + /// @brief 让当前进程在等待队列上进行等待,并且,不允许被信号打断 + pub fn sleep_uninterruptible(&self) { + let mut guard: SpinLockGuard = self.0.lock(); + current_pcb().state = PROC_UNINTERRUPTIBLE as u64; + guard.wait_list.push_back(current_pcb()); + drop(guard); + sched(); + } + + /// @brief 让当前进程在等待队列上进行等待,并且,允许被信号打断。 + /// 在当前进程的pcb加入队列后,解锁指定的自旋锁。 + pub fn sleep_unlock_spinlock(&self, to_unlock: SpinLockGuard) { + let mut guard: SpinLockGuard = self.0.lock(); + current_pcb().state = PROC_INTERRUPTIBLE as u64; + guard.wait_list.push_back(current_pcb()); + drop(to_unlock); + drop(guard); + sched(); + } + + /// @brief 让当前进程在等待队列上进行等待,并且,允许被信号打断。 + /// 在当前进程的pcb加入队列后,解锁指定的Mutex。 + pub fn sleep_unlock_mutex(&self, to_unlock: MutexGuard) { + let mut guard: SpinLockGuard = self.0.lock(); + current_pcb().state = PROC_INTERRUPTIBLE as u64; + guard.wait_list.push_back(current_pcb()); + drop(to_unlock); + drop(guard); + sched(); + } + + /// @brief 让当前进程在等待队列上进行等待,并且,不允许被信号打断。 + /// 在当前进程的pcb加入队列后,解锁指定的自旋锁。 + pub fn sleep_uninterruptible_unlock_spinlock(&self, to_unlock: SpinLockGuard) { + let mut guard: SpinLockGuard = self.0.lock(); + current_pcb().state = PROC_UNINTERRUPTIBLE as u64; + guard.wait_list.push_back(current_pcb()); + drop(to_unlock); + drop(guard); + sched(); + } + + /// @brief 让当前进程在等待队列上进行等待,并且,不允许被信号打断。 + /// 在当前进程的pcb加入队列后,解锁指定的Mutex。 + pub fn sleep_uninterruptible_unlock_mutex(&self, to_unlock: MutexGuard) { + let mut guard: SpinLockGuard = self.0.lock(); + current_pcb().state = PROC_UNINTERRUPTIBLE as u64; + guard.wait_list.push_back(current_pcb()); + drop(to_unlock); + drop(guard); + sched(); + } + + /// @brief 唤醒在队列中等待的第一个进程。 + /// 如果这个进程的state与给定的state进行and操作之后,结果不为0,则唤醒它。 + /// + /// @param state 用于判断的state,如果队列中第一个进程的state与它进行and操作之后,结果不为0,则唤醒这个进程。 + /// + /// @return true 成功唤醒进程 + /// @return false 没有唤醒进程 + pub fn wakeup(&self, state: u64) -> bool { + let mut guard: SpinLockGuard = self.0.lock(); + // 如果队列为空,则返回 + if guard.wait_list.is_empty() { + return false; + } + + // 如果队列头部的pcb的state与给定的state相与,结果不为0,则唤醒 + if (guard.wait_list.front().unwrap().state & state) != 0 { + let to_wakeup = guard.wait_list.pop_front().unwrap(); + unsafe { + process_wakeup(to_wakeup); + } + return true; + } else { + return false; + } + } + + /// @brief 获得当前等待队列的大小 + pub fn len(&self)->usize{ + return self.0.lock().wait_list.len(); + } +} + +impl InnerWaitQueue { + pub const INIT: InnerWaitQueue = InnerWaitQueue { + wait_list: LinkedList::new(), + }; +}