资源

  1. ucore在线实验指导书
  2. 我的ucore实验代码

练习1: 使用 Round Robin 调度算法(不需要编码)

题目

完成练习0后,建议大家比较一下(可用kdiff3等文件比较软件) 个人完成的lab5和练习0完成后的刚修改的lab6之间的区别,分析了解lab6采用RR调度算法后的执行过程。执行make grade,大部分测试用例应该通过。但执行priority.c应该过不去。

请在实验报告中完成:

请理解并分析sched_class中各个函数指针的用法,并结合Round Robin 调度算法描述ucore的调度执行过程。

请在实验报告中简要说明如何设计实现”多级反馈队列调度算法“,给出概要设计,鼓励给出详细设计。

解答

分析sched_class中各个函数指针的用法

  1. 首先,kern_init在调用vmm_init初始化完虚拟内存后,调用sched_init来初始化调度器。在sched_init函数中将sched赋值为default_sched_class。
    sched_class = &default_sched_class;
  1. default_sched_class的成员如下所示,接下来分析其中各个函数指针。
struct sched_class default_sched_class = {
.name = "RR_scheduler",
.init = RR_init,
.enqueue = RR_enqueue,
.dequeue = RR_dequeue,
.pick_next = RR_pick_next,
.proc_tick = RR_proc_tick,
};
  1. RR_init初始化RUNNABLE进程链表run_list,并将RUNNABLE进程的数目proc_num设置为0
    list_init(&(rq->run_list));
rq->proc_num = 0;
  1. RR_enqueue将一个新进程添加到RUNNABLE进程链表run_list的末尾,并将RUNNABLE进程数目proc_num加1。并且检查该进程的时间片,如果为0,重新为它分配时间片max_time_slice。此外还要保证进程的时间片不超过max_time_slice,如果超过了,将其修正为max_time_slice。
    list_add_before(&(rq->run_list), &(proc->run_link));
if (proc->time_slice == 0 || proc->time_slice > rq->max_time_slice) {
proc->time_slice = rq->max_time_slice;
}
proc->rq = rq;
rq->proc_num ++;
  1. RR_dequeue从RUNNABLE进程链表run_list中删除指定进程,并将RUNNABLE进程数目proc_num减1.
    list_del_init(&(proc->run_link));
rq->proc_num --;
  1. RR_pick_next从RUNNABLE进程链表run_list中取出首元素,然后返回。
    list_entry_t *le = list_next(&(rq->run_list));
if (le != &(rq->run_list)) {
return le2proc(le, run_link);
}
return NULL;
  1. RR_proc_tick将指定进程的时间片减1,如果减1后时间片数值为0,说明该进程的时间片用完了,需要被调度出去,因此将其need_resched标志设置为1.
    if (proc->time_slice > 0) {
proc->time_slice --;
}
if (proc->time_slice == 0) {
proc->need_resched = 1;
}

结合Round Robin调度算法描述ucore的调度执行过程

下面结合Round Robin调度算法来分析ucore系统的进程调度执行过程。

  1. 上文已经提到,ucore内核初始化总入口kern_init调用sched_init来初始化调度器sched_class,接下来调用proc_init来初始化进程。

  2. proc_init首先为当前正在运行的ucore程序分配一个进程控制块,并将其命名为idle,因此第一个内核线程idleproc应运而生。

  3. idleproc调用kernel_thread来创建一个新的内核线程initproc,kernel_thread进一步调用do_fork来完成具体的进程初始化操作,完成后调用wakeup_proc来唤醒新进程,并将内核线程initproc放在RUNNABLE队列rq的末尾。这时rq队列有了第一个进程在等待调度。

  4. proc_init结束后,继续一路运行到cpu_idle,在cpu_idle中,不断判断当前进程是否需要调度,如果需要则调用schedule进行调度。由于当前进程是idleproc,其need_resched设置为1,因此进入schedule进行调度。

  5. schedule首先判断当前进程是否RUNNABLE,以及是不是idleproc,如果当前进程不是idleproc而且RUNNABLE,则将其加入到rq队列的末尾。由于当前进程是idleproc,因此不会将其加入rq队列。

  6. 接下来从RUNNABLE队列中取出队首的进程(此时是initproc),通过调用proc_run来运行initproc进程。这时rq队列已空。

  7. initproc进程运行init_main,init_main调用kernel_thread来创建第三个进程userproc。同理,在完成userproc的初始化后,会调用wakeup_proc将其唤醒,并将其加入到rq队列的末尾。这时rq队列有一个进程userproc在等待调度。

  8. initproc进程接下来调用do_wait来等待子进程结束运行,其中搜索到其子进程userproc的state不为ZOMBIE,因此调用schedule来试图调度子进程来运行。由于rq队列只有一个进程initproc在排队,因此会调用idleproc来运行。这时rq队列又空了。另外注意,由于initproc进程在调用schedule之前将自己的state设置为SLEEPING,因此在进入schedule后,不会再次将其加入到rq队列,也就是说initproc需要睡眠了。什么时候睡醒呢?等子进程userproc运行结束后再将其唤醒。

  9. userproc进程运行user_main,加载ELF文件并运行之。运行完毕,则调用do_exit,在do_exit中,将自己的state设置为Zombie,然后调用wakeup_proc来唤醒initproc,这时会将initproc加入到rq队列,因此rq队列又有一个进程在等待了。接着调用schedule,选择刚加入的initproc来运行,rq队列再次变空。

  10. initproc回收子进程userproc的资源后,打印一些字符串信息,然后退出init_main,接下来进入do_exit,do_exit调用panic,panic停留在kmonitor界面一直等待用户输入。

设计实现“多级反馈队列调度算法”

暂时不会做。。

练习2: 实现 Stride Scheduling 调度算法(需要编码)

题目

首先需要换掉RR调度器的实现,即用default_sched_stride_c覆盖default_sched.c。然后根据此文件和后续文档对Stride调度器的相关描述,完成Stride调度算法的实现。后面的实验文档部分给出了Stride调度算法的大体描述。这里给出Stride调度算法的一些相关的资料(目前网上中文的资料比较欠缺)。你也可GOOGLE “Stride Scheduling” 来查找相关资料。

strid-shed paper location1

strid-shed paper location2

执行:make grade。如果所显示的应用程序检测都输出ok,则基本正确。如果只是priority.c过不去,可执行 make run-priority 命令来单独调试它。大致执行结果可看附录。(使用的是qemu-1.0.1 ) 。

请在实验报告中简要说明你的设计实现过程。

解答

我的设计实现过程

  1. 为了保留Round Robin和Stride两种调度算法,我并没有用default_sched_stride_c覆盖default_sched.c,而是保留两个文件,只是修改sched_init中绑定调度器的地方,将调度器绑定为stride_sched_class。

  2. stride_init:初始化RUNNABLE链表run_list(其实Stride Scheduling如果使用优先级队列时,不需要用到run_list,但这里还是一并初始化),初始化运行队列的运行池lab6_run_pool为空,初始化RUNNABLE进程数目为0.

  3. stride_enqueue:该函数将一个进程添加到运行队列。调用skew_heap_insert,将运行队列rq的运行进程池与新进程proc合并;将新进程的时间片初始化为最大值,关联proc->rq到rq,最后将rq的进程数目加1.

  4. stride_dequeue:该函数从运行队列中删除一个进程。只需调用skew_heap_remove,将进程proc从运行队列rq中删除,并取消proc->rq到rq的关联,最后将rq的进程数目减1.

  5. stride_pick_next:选择下一个要调度的进程,其实就保存在运行队列的头部。因此只需要调用le2proc根据rq->lab6_run_pool找到对应进程的地址,然后要更新对应进程的stride。

  6. stride_proc_tick:如果进程的时间片大于0,则将其减1。减1后如果等于0,则设置need_resched为1,表示该进程需要被调度出去。

代码优化

答案的代码中用调试宏开关来同时提供优先级队列和链表两种实现方式,可以参考。

扩展练习 Challenge 1 :实现 Linux 的 CFS 调度算法(待完成)

在ucore的调度器框架下实现下Linux的CFS调度算法。可阅读相关Linux内核书籍或查询网上资料,可了解CFS的细节,然后大致实现在ucore中。

扩展练习 Challenge 2 :实现更多基本调度算法(待完成)

在ucore上实现尽可能多的各种基本调度算法(FIFO, SJF,...),并设计各种测试用例,能够定量地分析出各种调度算法在各种指标上的差异,说明调度算法的适用范围。

《ucore lab6》实验报告的更多相关文章

  1. [操作系统实验lab3]实验报告

    [感受] 这次操作系统实验感觉还是比较难的,除了因为助教老师笔误引发的2个错误外,还有一些关键性的理解的地方感觉还没有很到位,这些天一直在不断地消化.理解Lab3里的内容,到现在感觉比Lab2里面所蕴 ...

  2. Ucore lab1实验报告

    练习一 Makefile 1.1 OS镜像文件ucore.img 是如何一步步生成的? + cc kern/init/init.c + cc kern/libs/readline.c + cc ker ...

  3. ucore操作系统学习(三) ucore lab3虚拟内存管理分析

    1. ucore lab3介绍 虚拟内存介绍 在目前的硬件体系结构中,程序要想在计算机中运行,必须先加载至物理主存中.在支持多道程序运行的系统上,我们想要让包括操作系统内核在内的各种程序能并发的执行, ...

  4. 《ucore lab3》实验报告

    资源 ucore在线实验指导书 我的ucore实验代码 练习1:给未被映射的地址映射上物理页 题目 完成do_pgfault(mm/vmm.c)函数,给未被映射的地址映射上物理页.设置访问权限的时候需 ...

  5. 《ucore lab1 exercise5》实验报告

    资源 ucore在线实验指导书 我的ucore实验代码 题目:实现函数调用堆栈跟踪函数 我们需要在lab1中完成kdebug.c中函数print_stackframe的实现,可以通过函数print_s ...

  6. 《ucore lab8》实验报告

    资源 ucore在线实验指导书 我的ucore实验代码 练习1: 完成读文件操作的实现(需要编码) 题目 首先了解打开文件的处理流程,然后参考本实验后续的文件读写操作的过程分析,编写在sfs_inod ...

  7. 《ucore lab7》实验报告

    资源 ucore在线实验指导书 我的ucore实验代码 练习1: 理解内核级信号量的实现和基于内核级信号量的哲学家就餐问题(不需要编码) 题目 完成练习0后,建议大家比较一下(可用meld等文件dif ...

  8. 《ucore lab5》实验报告

    资源 ucore在线实验指导书 我的ucore实验代码 练习1: 加载应用程序并执行(需要编码) 题目 do_execv函数调用load_icode(位于kern/process/proc.c中) 来 ...

  9. 《ucore lab4》实验报告

    资源 ucore在线实验指导书 我的ucore实验代码 练习1:分配并初始化一个进程控制块 题目 alloc_proc函数(位于kern/process/proc.c中) 负责分配并返回一个新的str ...

随机推荐

  1. Flower(规律+逆向思维)

    Flower: 传送门:http://acm.hdu.edu.cn/showproblem.php?pid=6486 题解: 逆向思维+规律 因为每次剪n-1,所以逆向就是控制n-1朵不变,每次增高1 ...

  2. [golang]golang如何覆盖输出console,实现进度条;golang一个骚气的进度提示库

    [golang]golang如何覆盖输出console,实现进度条 package main import( "fmt" "os" "time&quo ...

  3. Java IO管道流

    import java.io.IOException; import java.io.PipedInputStream; import java.io.PipedOutputStream; publi ...

  4. C语言的柔性数组的实现及应用

    c编程的时候数组长度一般都是固定好的,实际上c还能实现变长数组.其实c99中确实是有变长数组的说法,C99中通过允许结构体中的最后一个成员是长度未知的数组实现变长数组,其定义格式如下: typedef ...

  5. 数据结构Java版之邻接矩阵实现图(十一)

    邻接矩阵实现图,是用一个矩阵,把矩阵下标作为一个顶点,如果顶点与顶点之间有边.那么在矩阵对应的点上把值设为 1 .(默认是0) package mygraph; import java.util.Li ...

  6. 为什么要监控sql语句,以及如何监控,都有哪几种方式可以监控。

    快速阅读 为什么要监控sql语句,以及如何监控,都有哪几种方式可以监控. 我们知道sql server 中有个工具叫sql profile ,可以实时监控sql server中 执行的sql 语句,以 ...

  7. 蛋疼的springboot web项目使用jetty容器运行

    出现的问题: 今天自己新建了一个maven webapp项目,准备自己看看springboot的东西,搭好的项目是这样的 一切都很正常啊,用run App的方式直接启动 成功啦,本应该到此结束,喝茶吃 ...

  8. ubuntu之路——day8.2 深度学习优化算法之指数加权平均与偏差修正,以及基于指数加权移动平均法的动量梯度下降法

    首先感谢吴恩达老师的免费公开课,以下图片均来自于Andrew Ng的公开课 指数加权平均法 在统计学中被称为指数加权移动平均法,来看下面一个例子: 这是伦敦在一些天数中的气温分布图 Vt = βVt- ...

  9. 咏南中间件随LINUX开机自动启动

    咏南中间件随LINUX开机自动启动 LINUX DEAMON,LINUX也有类似WINDOWS的服务. program ynCrossServer; {$HINTS OFF} {$APPTYPE CO ...

  10. Spark(四十六):Spark 内存管理之—OFF_HEAP

    存储级别简介 Spark中RDD提供了多种存储级别,除去使用内存,磁盘等,还有一种是OFF_HEAP,称之为 使用JVM堆外内存 https://github.com/apache/spark/blo ...