first create lab6_report.md, not finished yet.

This commit is contained in:
Shine wOng
2019-09-17 11:29:57 +08:00
parent b78d975a9d
commit 02084006c9

68
thu_os/lab6_report.md Normal file
View File

@@ -0,0 +1,68 @@
Lab6 Report
===========
## 实验目的
+ 理解操作系统的调度管理机制
+ 熟悉`ucore`的系统调度器框架,以及缺省的`Round-Robin`调度算法
+ 基于调度器框架实现一个`Stride Scheduling`调度算法来替换缺省的调度算法
## 实验内容
实验五完成了用户进程的管理,可在用户态运行多个进程。但到目前为止,采用的调度策略是很简单的`FIFO`调度策略。本次实验,主要是熟悉`ucore`的系统调度器框架,以及基于此框架的`Round-RobinRR` 调度算法。然后参考`RR`调度算法的实现,完成`Stride Scheduling`调度算法。
## 练习
对实验报告的要求:
+ 基于markdown格式来完成以文本方式为主
+ 填写各个基本练习中要求完成的报告内容
+ 完成实验后,请分析`ucore lab`中提供的参考答案,并请在实验报告中说明你的实现与参考答案的区别
+ 列出你认为本实验中重要的知识点以及与对应的OS原理中的知识点并简要说明你对二者的含义关系差异等方面的理解也可能出现实验中的知识点没有对应的原理知识点
+ 列出你认为OS原理中很重要但在实验中没有对应上的知识点
## lab6概述
首先简单叙述一下lab6都完成了或者需要完成哪些工作。
lab6最重要的工作就是实现了进程调度的框架类并且基于这个框架更新了有关进程调度的一些函数的实现比如`schedule`,还有`wake_proc`等。
此外使用这个进程调度框架可以实现原理课中讲过的各种进程调度算法在lab6中是实现了要求实现`RR`调度算法以及`Stride`调度算法。
## 进程调度框架的实现
为了实现一个一般的进程调度框架,我们需要考虑不同的进程调度算法所具有的共性,将这些共性抽象出来形成代码,就成为了我们这里的进程调度框架。
对于任意一个进程调度算法,在进行调度`schedule`时,都需要进行两方面的工作,即从就绪队列中选择一个新的进程投入运行,并且将当前进程(如果仍然就绪的话)重新加入就绪队列中。因此,这里就涉及到两个基本的操作,即从就绪队列中选取一个进程(`pick`),以及将一个进程加入到就绪队列中(`enqueue`)。实际上,这里还隐含的一个操作,即将被选取的进程从就绪队列中删除,即`dequeue`操作。
上面的讨论只是关于具体的进程调度的,现在再来考虑以下其他和进程调度有关的情形。
+ 在`fork`一个新的进程时,在`do_fork`函数中需要将新创建的进程加入就绪队列中,可以调用上面提到的`enqueue`操作;
+ 当进程等待资源或者等待子进程退出时,需要将自己的状态标记为等待态(`WAITING`),并且调用`schedule`函数,在`schedule`函数中将完成其余实质性的工作;
+ 当进程退出时,需要将自己的状态标记为僵尸态(`ZOMBIE`),随后调用`schedule`函数,完成剩余的工作。
+ 当进程因为获得等待的资源而被唤醒时,应该将该进程的状态标记为就绪态(`RUNNING`),并且将它放入就绪队列中,可以调用`enqueue`来完成这个操作。
可以看到,上面抽象出来的三个基本操作,几乎已经可以满足进程调度中可能出现的所有情况了,因此只要把这些操作封装起来,就基本实现了我们这里的进程调度框架。如下面的代码所示:
```c
struct sched_class {
// the name of sched_class
const char *name;
// Init the run queue
void (*init)(struct run_queue *rq);
// put the proc into runqueue, and this function must be called with rq_lock
void (*enqueue)(struct run_queue *rq, struct proc_struct *proc);
// get the proc out runqueue, and this function must be called with rq_lock
void (*dequeue)(struct run_queue *rq, struct proc_struct *proc);
// choose the next runnable task
struct proc_struct *(*pick_next)(struct run_queue *rq);
}
```
这里实现的进程调度类和上面我们描述的几乎是对应的,只是添加了`name`字段来保存当前使用的进程调度算法的名字,以及`init`函数来对相关数据结构进行初始化。
但是这里实现的进程调度类的确已经可以满足所有的进程调度算法了吗?考虑时间片轮转算法,我们会为每个进程分配一个固定的时间片,一旦这个时间片用完,就对当前的进程进行调度。这里的问题是,如何在进程执行的过程中更新这个时间片呢?
显然,这个工作只能由操作系统来完成,因为和进程有关的控制信息是保存在内核的,并且让用户进程本身来管理这些控制信息也比较危险。一种解决方案是由时钟定期地产生中断,在时钟中断的处理函数对进程的时间片进行更新。
事实上,不只是`RR`算法,其他进程调度算法也需要在运行过程中动态地更新一些参数。例如最高响应比优先算法`HRRN`,也需要在执行中动态更新每个进程的等待时间,来计算每个进程的响应比。因此,我们这里的进程调度框架还需要一个参数更新的函数,来在运行时动态地更新调度算法的参数。