• 练习0 填写已有实验
  • 练习1 使用Round Robin调度算法
    • 0x1 执行过程
    • 0x2 算法实现
  • 练习2 实现Stride Scheduling调度算法
  • 实验结果
  • 实验心得

练习0 填写已有实验

meld的软件进行对比即可
现在将需要修改的文件罗列如下:

proc.c
default_pmm.c
pmm.c
swap_fifo.c
vmm.c
trap.c

根据注释的提示,主要是一下两个函数需要额外加以修改。


alloc_proc函数
完整代码如下:

static struct proc_struct *
alloc_proc(void) {struct proc_struct *proc = kmalloc(sizeof(struct proc_struct));if (proc != NULL) {proc->state = PROC_UNINIT; //设置进程初始状态proc->pid = -1;            //进程id=-1proc->runs = 0;            //初始化时间片为0proc->kstack = 0;          //初始化内存栈的地址为0proc->need_resched = 0;    //是否需要调度设为不需要proc->parent = NULL;       //将父节点置空proc->mm = NULL;           //置空虚拟内存memset(&(proc->context), 0, sizeof(struct context));//初始化上下文proc->tf = NULL;           //将中断帧指针置空proc->cr3 = boot_cr3;      //将页目录设内核页目录表的基址proc->flags = 0;           //初始化标志位memset(proc->name, 0, PROC_NAME_LEN);//置空进程名proc->wait_state = 0;      //初始化进程等待状态  proc->cptr=proc->yptr=proc->optr = NULL;//初始化进程相关指针  proc->rq = NULL;//置空运行队列 list_init(&(proc->run_link));//初始化运行队列的指针proc->time_slice = 0;      //初始化时间片proc->lab6_run_pool.left = proc->lab6_run_pool.right = proc->lab6_run_pool.parent = NULL; //初始化各类指针为空proc->lab6_stride = 0;     //初始化当前运行步数proc->lab6_priority = 0;   //初始化优先级}return proc;
}

相比于lab5,lab6对proc_struct结构体再次做了扩展,这里主要是多出了以下部分

proc->rq = NULL;             //初始化运行队列为空list_init(&(proc->run_link));//初始化运行队列的指针proc->time_slice = 0;        //初始化时间片proc->lab6_run_pool.left = proc->lab6_run_pool.right proc->lab6_run_pool.parent = NULL; //初始化各类指针为空,包括父进程等待proc->lab6_stride = 0;       //步数初始化 proc->lab6_priority = 0;     //初始化优先级

trap_dispatch函数

static void
trap_dispatch(struct trapframe *tf) {............ticks ++;  assert(current != NULL);  run_timer_list(); //更新定时器,并根据参数调用调度算法  break;  ............
}

练习1 使用Round Robin调度算法

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

0x1 执行过程

让所有runnable态的进程分时轮流使用CPU时间。RR调度器维护当前runnable进程的有序运行队列。当前进程的时间片用完之后,调度器将当前进程放置到运行队列的尾部,再从其头部取出进程进行调度。RR调度算法的就绪队列在组织结构上也是一个双向链表,只是增加了一个成员变量,表明在此就绪进程队列中的最大执行时间片。而且在进程控制块proc_struct中增加了一个成员变量time_slice,用来记录进程当前的可运行时间片段。这是由于RR调度算法需要考虑执行进程的运行时间不能太长。在每个timer到时的时候,操作系统会递减当前执行进程的time_slice,当time_slice为0时,就意味着这个进程运行了一段时间(这个时间片段称为进程的时间片),需要把CPU让给其他进程执行,于是操作系统就需要让此进程重新回到rq的队列尾,且重置此进程的时间片为就绪队列的成员变量最大时间片max_time_slice值,然后再从rq的队列头取出一个新的进程执行。

0x2 算法实现

RR_init完成了对进程队列的初始化

    static void  RR_init(struct run_queue *rq) {  list_init(&(rq->run_list));  rq->proc_num = 0;  }  

RR_enqueue的函数实现如下表所示。即把某进程的进程控制块指针放入到rq队列末尾,且如果进程控制块的时间片为0,则需要把它重置为rq成员变量max_time_slice。这表示如果进程在当前的执行时间片已经用完,需要等到下一次有机会运行时,才能再执行一段时间。

    static void  RR_enqueue(struct run_queue *rq, struct proc_struct *proc) {  assert(list_empty(&(proc->run_link)));  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 ++;  }  

RR_dequeue的函数实现如下表所示。即把就绪进程队列rq的进程控制块指针的队列元素删除,并把表示就绪进程个数的proc_num减一。

    static void  RR_dequeue(struct run_queue *rq, struct proc_struct *proc) {  assert(!list_empty(&(proc->run_link)) && proc->rq == rq);  list_del_init(&(proc->run_link));  rq->proc_num --;  }  

RR_pick_next的函数实现如下表所示。即选取就绪进程队列rq中的队头队列元素,并把队列元素转换成进程控制块指针。

    static struct proc_struct *  RR_pick_next(struct run_queue *rq) {  list_entry_t *le = list_next(&(rq->run_list));  if (le != &(rq->run_list)) {  return le2proc(le, run_link);  }  return NULL;  }  

RR_proc_tick的函数实现如下表所示。即每次timer到时后,trap函数将会间接调用此函数来把当前执行进程的时间片time_slice减一。如果time_slice降到零,则设置此进程成员变量need_resched标识为1,这样在下一次中断来后执行trap函数时,会由于当前进程程成员变量need_resched标识为1而执行schedule函数,从而把当前执行进程放回就绪队列末尾,而从就绪队列头取出在就绪队列上等待时间最久的那个就绪进程执行。

static void RR_proc_tick(struct run_queue *rq, struct proc_struct *proc) {  if (proc->time_slice > 0) {  proc->time_slice --;  }  if (proc->time_slice == 0) {  proc->need_resched = 1;  }
}  

练习2 实现Stride Scheduling调度算法

首先需要换掉RR调度器的实现,即用default_sched_stride_c覆盖default_sched.c。然后根据此文件和后续文档对Stride度器的相关描述,完成Stride调度算法的实现。

首先,根据实验指导书的要求,先用default_sched_stride_c覆盖default_sched.c,即覆盖掉Round Robin调度算法的实现。
覆盖掉之后需要在该框架上实现Stride Scheduling调度算法。
关于Stride Scheduling调度算法,经过查阅资料和实验指导书,我们可以简单的把思想归结如下:

1、为每个runnable的进程设置一个当前状态stride,表示该进程当前的调度权。另外定义其对应的pass值,表示对应进程在调度后,stride 需要进行的累加值。
2、每次需要调度时,从当前 runnable 态的进程中选择 stride最小的进程调度。对于获得调度的进程P,将对应的stride加上其对应的步长pass(只与进程的优先权有关系)。
3、在一段固定的时间之后,回到步骤2,重新调度当前stride最小的进程

首先是初始化函数stride_init。
开始初始化运行队列,并初始化当前的运行队,然后设置当前运行队列内进程数目为0。

static void
stride_init(struct run_queue *rq) {  /* LAB6: YOUR CODE */  list_init(&(rq->run_list)); rq->lab6_run_pool = NULL; rq->proc_num = 0;
}  

然后是入队函数stride_enqueue,根据之前对该调度算法的分析,这里函数主要是初始化刚进入运行队列的进程 proc 的stride属性,然后比较队头元素与当前进程的步数大小,选择步数最小的运行,即将其插入放入运行队列中去,这里并未放置在队列头部。最后初始化时间片,然后将运行队列进程数目加一。

static void
stride_enqueue(struct run_queue *rq, struct proc_struct *proc) {  /* LAB6: YOUR CODE */
#if USE_SKEW_HEAP  rq->lab6_run_pool = //在使用优先队列的实现中表示当前优先队列的头元素  skew_heap_insert(rq->lab6_run_pool, &(proc->lab6_run_pool), proc_stride_comp_f);//比较队头元素与当前进程的步数大小,选择步数最小的运行
#else  assert(list_empty(&(proc->run_link)));  list_add_before(&(rq->run_list), &(proc->run_link));//将 proc插入放入运行队列中去
#endif  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 ++;
}  

然后是出队函数stride_dequeue,即完成将一个进程从队列中移除的功能,这里使用了优先队列。最后运行队列数目减一。

static void
stride_dequeue(struct run_queue *rq, struct proc_struct *proc) {  /* LAB6: YOUR CODE */
#if USE_SKEW_HEAP  rq->lab6_run_pool =   skew_heap_remove(rq->lab6_run_pool, &(proc->lab6_run_pool), proc_stride_comp_f);// 在斜堆中删除相应元素
#else  assert(!list_empty(&(proc->run_link)) && proc->rq == rq);  list_del_init(&(proc->run_link));// 从运行队列中删除相应元素
#endif  rq->proc_num --;
}  

接下来就是进程的调度函数stride_pick_next,观察代码,它的核心是先扫描整个运行队列,返回其中stride值最小的对应进程,然后更新对应进程的stride值,将步长设置为优先级的倒数,如果为0则设置为最大的步长。

static struct proc_struct *
stride_pick_next(struct run_queue *rq) {/* LAB6: YOUR CODE */
#if USE_SKEW_HEAPif (rq->lab6_run_pool == NULL) return NULL;struct proc_struct *p = le2proc(rq->lab6_run_pool, lab6_run_pool);
#elselist_entry_t *le = list_next(&(rq->run_list));if (le == &rq->run_list)return NULL;struct proc_struct *p = le2proc(le, run_link);le = list_next(le);while (le != &rq->run_list){struct proc_struct *q = le2proc(le, run_link);if ((int32_t)(p->lab6_stride - q->lab6_stride) > 0)p = q;le = list_next(le);}
#endif//更新对应进程的stride值if (p->lab6_priority == 0)//优先级设置  p->lab6_stride += BIG_STRIDE;//步长为0则设置为最大步长保持相减的有效性  else p->lab6_stride += BIG_STRIDE / p->lab6_priority;//步长设置为优先级的倒数  return p;
}

函数stride_proc_tick的主要工作是检测当前进程的时间片是否已经用完。如果时间片已经用完,就会按照正确的流程进行进程的切换工作。这里和之前实现的Round Robin调度算法一样,所采用的思想也是一致的

优先队列比较函数proc_stride_comp_f的实现,主要利用思路是通过相减之后的值,进行判断大小

static int
proc_stride_comp_f(void *a, void *b)
{  struct proc_struct *p = le2proc(a, lab6_run_pool);  struct proc_struct *q = le2proc(b, lab6_run_pool);  int32_t c = p->lab6_stride - q->lab6_stride;//步数相减,通过正负比较大小关系  if (c > 0) return 1;  else if (c == 0) return 0;  else return -1;
}  

实验结果

实验心得

通过这一次实验我对轮转法以及stride法有了更多的了解,stride法其实就是轮转法的一种升级。stride法加入了对进程优先级的调整,步数越小,进程优先级越大。这种改变更加合理。进程的调度极大的提高了CPU的利用率。更加深刻的理解了进程切换的原理,对Stride Schedule算法的原理和算法可控性和确定性有了更深的认识。

操作系统实验报告 lab6相关推荐

  1. 实验报告Linux操作系统基本命令,linux操作系统实验报告全部.doc

    linux操作系统实验报告全部 计算机操作系统 实验报告 学 号:姓 名:提交日期:2014.12.15成 绩: 东北大学秦皇岛分校 [实验题目]熟悉Linux/UNIX操作系统[实验目的]1.熟悉L ...

  2. Linux进程的创建和父子进程同步,操作系统实验报告_Linux进程创建与通信.doc

    操作系统实验报告_Linux进程创建与通信 2011-2012学年第一学期 专 业: 班 级: 学 号: 姓 名:提交日期:2011年11月实验二 Linux进程创建与进程通信 [实验目的 1. 熟悉 ...

  3. 计算机操作系统安装实验报告,计算机操作系统实验报告.doc

    计算机操作系统实验报告.doc (12页) 本资源提供全文预览,点击全文预览即可全文预览,如果喜欢文档就下载吧,查找使用更方便哦! 14.9 积分 计算机操作系统实验报告实验一一.实验目的 在单处理 ...

  4. 操作系统实验报告18:硬盘柱面访问调度算法

    操作系统实验报告18 实验内容 实验内容:硬盘调度. 编写 C 程序模拟实现课件 Lecture25 中的硬盘柱面访问调度算法 包括 FCFS.SSTF.SCAN.C-SCAN.LOOK.C-LOOK ...

  5. 操作系统实验报告17:请求页面置换算法

    操作系统实验报告17 实验内容 实验内容:虚拟存储管理. 编写一个 C 程序模拟实现课件 Lecture24 中的请求页面置换算法 包括FIFO.LRU (stack and matrix imple ...

  6. 操作系统实验报告16:CPU 调度

    操作系统实验报告16 实验内容 实验内容:CPU 调度. 讨论课件 Lecture19-20 中 CPU 调度算法的例子,尝试基于 POSIX API 设计一个简单调度器(不考虑资源竞争问题): 创建 ...

  7. 操作系统实验报告15:进程同步与互斥线程池

    操作系统实验报告15 实验内容 实验内容:进程同步. 内容1:编译运行课件 Lecture18 例程代码. Algorithms 18-1 ~ 18-9. 内容2:在 Lab Week 13 的基础上 ...

  8. 操作系统实验报告14:Peterson 算法

    操作系统实验报告14 实验内容 实验内容:Peterson 算法. 把 Lecture08 示例 alg.8-1~8-3 拓展到多个读线程和多个写线程,应用 Peterson 算法原理设计实现共享内存 ...

  9. 操作系统实验报告13:线程池简单实现

    操作系统实验报告13 实验内容 实验内容:设计实现一个线程池 (Thread Pool) 使用 Pthread API FIFO 先不考虑互斥问题 编译.运行.测试用例 实验环境 架构:Intel x ...

  10. 操作系统实验报告12:线程2

    操作系统实验报告12 实验内容 实验内容:线程(2). 编译运行课件 Lecture14 例程代码: Algorithms 14-1 ~ 14-7. 比较 pthread 和 clone() 线程实现 ...

最新文章

  1. UITextView高度根据内容变化
  2. ibatis基础(三):查询指定id的单个对象
  3. 基于XMPP协议的aSmack源码分析
  4. 使用ps命令输出进程列表--用Enki学Linux系列(17)
  5. 实现cookie跨域
  6. python模式字符串_使用python进行字符串模式匹配
  7. c语言有趣的编程代码,一个有趣的小程序
  8. 使用普通asp.net编程方式开发WAP应用的可行性
  9. java 多个引用指向同一个对象(含案例图解)
  10. f-stack nginx 单进程模式启动流程分析
  11. 字符串系列——uva10361 - 自动作诗机
  12. 计算机术语中bug指的是,BUG是什么意思?为什么用bug代指漏洞?
  13. 对抗样本生成算法之DeepFool算法
  14. 测量网页元素的大小和间距的利器
  15. 办公自动化工具-Excel文件、sheet合并工具
  16. 光猫及二级路由器Openwrt均开启IPv6,满足双层网络内IPv6的获取
  17. app store 注册账号生成证书上传app完整的教程
  18. javascript join()
  19. Spring+SpringMVC+Hibernate整合(封装CRUD操作)
  20. Linux 系统管理没有 netstat ,不惆怅! 拥抱 ss ,事半功倍

热门文章

  1. Windows给SVN配置中文语言包
  2. 教之初计算机考试试题,教之初题库管理系统操作教程-考题处理
  3. c语言构造数学模型步骤,数学建模的6个基本步骤
  4. android ide 下载
  5. android 获取高德地图坐标,获取地址描述数据-获取地图数据-开发指南-Android 地图SDK | 高德地图API...
  6. Vensim模拟结果vdf数据文件转化为可读文本文件
  7. 使用GWmodel进行GWR模型相关运算
  8. 《推荐系统实践》(一)好的推荐系统
  9. Syncthing:开源 P2P 文件同步工具
  10. 系统集成项目管理工程师14 总结