线程调度之 Round Robin 算法

时间片轮转调度算法(Round Robin)的基本思想是让每个线程在就绪队列中的等待时间与占用 CPU 的执行时间成正比例。其大致实现是:

  1. 将系所有的就绪线程按照 FCFS 原则,排成一个就绪队列。
  2. 每次调度时将 CPU 分派(dispatch)给队首进程,让其执行一个时间片。
  3. 在时钟中断时,统计比较当前线程时间片是否已经用完。  - 如用完,则调度器(scheduler)暂停当前进程的执行,将其送到就绪队列的末尾,并通过切换执行就绪队列的队首进程。  - 如没用完,则线程继续使用。

对于不同的调度算法,我们实现了一个调度接口框架如下:

  1. pub trait Scheduler {
  2. fn push(&mut self, tid: Tid);    //把Tid线程放入就绪队列
  3. fn pop(&mut self) -> Option<Tid>;  //从就绪队列取出线程
  4. fn tick(&mut self) -> bool;     //时钟tick(代表时间片)处理
  5. fn exit(&mut self, tid: Tid);    //线程退出
  6. }

时间片轮转调度算法对上述四个函数接口有具体的实现。这里我们直接给出时间片轮转调度算法的实现代码,有兴趣者可自行去研究算法细节。

  1. // src/process/scheduler.rs
  2. use alloc::vec::Vec;
  3. #[derive(Default)]
  4. struct RRInfo {
  5. valid: bool,
  6. time: usize,
  7. prev: usize,
  8. next: usize,
  9. }
  10. pub struct RRScheduler {
  11. threads: Vec<RRInfo>,
  12. max_time: usize,
  13. current: usize,
  14. }
  15. impl RRScheduler {
  16. // 设置每个线程连续运行的最大 tick 数
  17. pub fn new(max_time_slice: usize) -> Self {
  18. let mut rr = RRScheduler {
  19. threads: Vec::default(),
  20. max_time: max_time_slice,
  21. current: 0,
  22. };
  23. rr.threads.push(
  24. RRInfo {
  25. valid: false,
  26. time: 0,
  27. prev: 0,
  28. next: 0,
  29. }
  30. );
  31. rr
  32. }
  33. }
  34. impl Scheduler for RRScheduler {
  35. // 分为 1. 新线程 2. 时间片耗尽被切换出的线程 两种情况
  36. fn push(&mut self, tid : Tid) {
  37. let tid = tid + 1;
  38. if tid + 1 > self.threads.len() {
  39. self.threads.resize_with(tid + 1, Default::default);
  40. }
  41. if self.threads[tid].time == 0 {
  42. self.threads[tid].time = self.max_time;
  43. }
  44. let prev = self.threads[0].prev;
  45. self.threads[tid].valid = true;
  46. self.threads[prev].next = tid;
  47. self.threads[tid].prev = prev;
  48. self.threads[0].prev = tid;
  49. self.threads[tid].next = 0;
  50. }
  51. fn pop(&mut self) -> Option<Tid> {
  52. let ret = self.threads[0].next;
  53. if ret != 0 {
  54. let next = self.threads[ret].next;
  55. let prev = self.threads[ret].prev;
  56. self.threads[next].prev = prev;
  57. self.threads[prev].next = next;
  58. self.threads[ret].prev = 0;
  59. self.threads[ret].next = 0;
  60. self.threads[ret].valid = false;
  61. self.current = ret;
  62. Some(ret-1)
  63. }else{
  64. None
  65. }
  66. }
  67. // 当前线程的可用时间片 -= 1
  68. fn tick(&mut self) -> bool{
  69. let tid = self.current;
  70. if tid != 0 {
  71. self.threads[tid].time -= 1;
  72. if self.threads[tid].time == 0 {
  73. return true;
  74. }else{
  75. return false;
  76. }
  77. }
  78. return true;
  79. }
  80. fn exit(&mut self, tid : Tid) {
  81. let tid = tid + 1;
  82. if self.current == tid {
  83. self.current = 0;
  84. }
  85. }
  86. }