mirror of
https://github.com/DragonOS-Community/DragonOS.git
synced 2025-06-09 02:46:47 +00:00
parent
3c369b1430
commit
151251b50b
@ -1,4 +1,10 @@
|
||||
# 与“等待”相关的api
|
||||
# 与“等待”相关的api(C语言)
|
||||
|
||||
:::{warning}
|
||||
|
||||
随着内核的发展,我们将会逐步将C语言的等待机制替换为Rust语言的等待机制。在这个过程中,我们将会同时保留C语言和Rust语言的等待机制,以便于我们在开发过程中进行对比。
|
||||
待时机成熟,我们将会逐步将C语言的等待机制移除。
|
||||
:::
|
||||
|
||||
  如果几个进程需要等待某个事件发生,才能被运行,那么就需要一种“等待”的机制,以实现进程同步。
|
||||
|
@ -7,4 +7,5 @@ DragonOS调度
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
waiting
|
||||
c_waiting
|
||||
rust_waiting
|
||||
|
81
docs/kernel/sched/rust_waiting.md
Normal file
81
docs/kernel/sched/rust_waiting.md
Normal file
@ -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() | 返回等待队列中的进程数量 |
|
||||
|
||||
|
@ -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)]
|
||||
|
@ -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(),
|
||||
};
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user