Browse Source

Patch add rust waitqueue (#162)

* new: rust版本的waitqueue

* new:等待队列的文档
login 2 years ago
parent
commit
151251b50b

+ 7 - 1
docs/kernel/sched/waiting.md → docs/kernel/sched/c_waiting.md

@@ -1,4 +1,10 @@
-# 与“等待”相关的api
+# 与“等待”相关的api(C语言)
+
+:::{warning}
+
+随着内核的发展,我们将会逐步将C语言的等待机制替换为Rust语言的等待机制。在这个过程中,我们将会同时保留C语言和Rust语言的等待机制,以便于我们在开发过程中进行对比。
+待时机成熟,我们将会逐步将C语言的等待机制移除。
+:::
 
   如果几个进程需要等待某个事件发生,才能被运行,那么就需要一种“等待”的机制,以实现进程同步。
 

+ 2 - 1
docs/kernel/sched/index.rst

@@ -7,4 +7,5 @@ DragonOS调度
 .. toctree::
    :maxdepth: 1
 
-   waiting
+   c_waiting
+   rust_waiting

+ 81 - 0
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() | 返回等待队列中的进程数量 |
+
+

+ 1 - 1
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)]

+ 128 - 2
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<InnerWaitQueue>);
+
+impl WaitQueue {
+    pub const INIT: WaitQueue = WaitQueue(SpinLock::new(InnerWaitQueue::INIT));
+
+    /// @brief 让当前进程在等待队列上进行等待,并且,允许被信号打断
+    pub fn sleep(&self) {
+        let mut guard: SpinLockGuard<InnerWaitQueue> = 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<InnerWaitQueue> = 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<T>(&self, to_unlock: SpinLockGuard<T>) {
+        let mut guard: SpinLockGuard<InnerWaitQueue> = 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<T>(&self, to_unlock: MutexGuard<T>) {
+        let mut guard: SpinLockGuard<InnerWaitQueue> = 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<T>(&self, to_unlock: SpinLockGuard<T>) {
+        let mut guard: SpinLockGuard<InnerWaitQueue> = 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<T>(&self, to_unlock: MutexGuard<T>) {
+        let mut guard: SpinLockGuard<InnerWaitQueue> = 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<InnerWaitQueue> = 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(),
+    };
+}