stride调度算法实现
简述
- 在
TaskControlBlockInner
中加入stride
和priority
两个字段,这里我选择的类型是usize
- 接下来将
TaskManager
中的任务队列更换为BinaryHeap
类型 (官方文档)。 - 为了能进行比较,还需要为
TaskControlBlock
实现Ord trait
(官方文档) - 最后,修改
task/processor.rs
中的run_tasks()
函数,使其在调度任务时,计算pass
并增加对应task的stride
实现
// os/src/task/task.rs
use core::cmp::Ordering;
impl Ord for TaskControlBlock {
fn cmp(&self, other: &Self) -> Ordering {
let self_stride = self.inner_exclusive_access().get_stride() as isize;
let other_stride = other.inner_exclusive_access().get_stride() as isize;
// Min heap need reverse sub
let cmp_res = other_stride.wrapping_sub(self_stride);
// use isize to process stride overflow
if cmp_res < 0 {
Ordering::Less
} else if cmp_res > 0 {
Ordering::Greater
} else {
Ordering::Equal
}
}
}
impl PartialOrd for TaskControlBlock {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Eq for TaskControlBlock {}
impl PartialEq for TaskControlBlock {
fn eq(&self, other: &Self) -> bool {
let self_stride = self.inner_exclusive_access().get_stride();
let other_stride = other.inner_exclusive_access().get_stride();
self_stride == other_stride
}
}
这里需要注意两点:
- 考虑到
stride
溢出问题(需要一定计组知识),需要将无符号数转为有符号数后再进行判断,详细分析见 uCore stride算法
这里将实验书中
u8
的例子解释下 ,i8
取值范围为 [ − 128 , 127 ] \left [-128, 127 \right ] [−128,127]
u8 → \to → i8
125
(0x7D) → \to →125
(0x7D)
255
(0xFF) → \to →-1
(0xFF 补码表示) (0x81 原码表示)
于是125 - 255
→ \to →125 - (-1) = 126 > 0
所以 (125 < 255) == false
129
(0x81) → \to →-127
((0x81 补码表示) (0xFF 原码表示)
255
(0xFF) → \to →-1
(0xFF 补码表示) (0x81 原码表示)
于是129 - 255
→ \to →-127 - (-1) = -126 < 0
所以 (129 < 255) == true
再用下面这张图解释一下,u8
到i8
的转换就是这样一个映射关系
无符号数
[
0
,
127
]
\left [0, 127 \right ]
[0,127] 对应有符号数
[
0
,
127
]
\left [0, 127 \right ]
[0,127]
无符号数
[
128
,
255
]
\left [128, 255 \right ]
[128,255] 对应有符号数
[
−
128
,
−
1
]
\left [-128, -1 \right ]
[−128,−1]
当两个stride
分别处于不同颜色区域时,在蓝色部分的数就是发生溢出的,真实stride
要比处在橙色部分要大;通过有符号数的转换,两个数大小关系恢复到真实值(即u8
蓝色溢出要比橙色大)
- 由于
BinaryHeap
默认为大根堆,cmp
函数需要用other
减去self
来实现小根堆。使用wrapping_sub
也是为了防止溢出
pub const BIG_STRIDE: usize = usize::MAX >> 1;
// os/src/task/processor.rs
pub fn run_tasks() {
loop {
let mut processor = PROCESSOR.exclusive_access();
if let Some(task) = fetch_task() {
let idle_task_cx_ptr = processor.get_idle_task_cx_ptr();
// access coming task TCB exclusively
let mut task_inner = task.inner_exclusive_access();
let next_task_cx_ptr = &task_inner.task_cx as *const TaskContext;
task_inner.task_status = TaskStatus::Running;
let pass = BIG_STRIDE / task_inner.get_priority();
let task_stride = task_inner.get_stride();
task_inner.stride = task_stride.wrapping_add(pass);
drop(task_inner);
// release coming task TCB manually
processor.current = Some(task);
// release processor manually
drop(processor);
unsafe {
__switch(idle_task_cx_ptr, next_task_cx_ptr);
}
}
}
}
由于要求priority >= 2
,所以pass = BIG_STRIDE / priority <= usize::MAX / 2
,所以将BIG_STRIDE
设置为该值
实验结果
Max raito / Min raito = 2_387_666 / 2_315_200 = 1.03 < 1.5
通过测试
总结
实验主要难点在于stride
溢出问题,我解释了下u8
的例子并用一张图说明了无符号数到有符号数的转换,希望有助于大家理解