CPU调度

从就绪队列中挑选下一个占用CPU运行的进程,从多个可用CPU中挑选就绪进程可使用的CPU资源

调度策略

比较调度算法的准则

  • CPU使用率
  • 吞吐量
  • 周转时间
  • 就绪等待时间
  • 响应时间

吞吐量与延迟

  1. 低延迟:喝水的时候想要一打开水龙头水就流出来

  2. 高带宽:给游泳池充水时希望从水龙头里同时流出大量的水,并且不介意是否存在延迟

处理机调度策略的响应时间目标

  1. 减少响应时间
  2. 减少平均响应时间的波动
  3. 增加吞吐量
  4. 减少等待时间

调度算法

先来先服务算法(First Come First Served, FCFS)

依据进程进入就绪状态的先后顺序排列,进程进入等待或结束状态时,就绪队列中的下一个进程占用CPU

但是FCFS的平均等待时间波动较大,I/O资源和CPU资源的利用率较低

短进程优先算法(SPN)

选择就绪队列中执行时间最短进程占用CPU进入运行状态,用历史的执行时间来预估未来的执行时间,短进程优先算法具有最优平均周转时间

但是连续的短进程流会使长进程无法获得CPU资源

最高响应比优先算法(HRRN)

选择就绪队列中响应比R值最高的进程

R=(w+s)/s

    w: 等待时间(waiting time)
s: 执行时间(service time)

时间片轮转算法(RR, Round-Robin)

利用时间片作为分配处理机资源的基本时间单元,时间片结束时,按FCFS算法切换到下一个就绪进程, 每隔(n – 1)个时间片进程执行一个时间片q

但是时间片轮转算法需要选择好时间片的大小,过大过小都会导致效率问题

多级队列调度算法(MQ)

就绪队列被划分成多个独立的子队列,如:前台–RR、后台–FCFS

多级反馈队列算法(MLFQ)

进程可在不同队列间移动的多级队列算法,时间片大小随优先级级别增加而增加,如进程在当前的时间片没有完成,则降到下一个优先级

公平共享调度(FSS, Fair Share Scheduling)

FSS控制用户对系统资源的访问,一些用户组比其他用户组更重要,保证不重要的组无法垄断资源,未使用的资源按比例分配,没有达到资源使用率目标的组获得更高的优先级

代码实现

这个实验其实有两个,一个是实现Round Robin,一个是Stride Scheduling,两个都非常简单。

Round Robin调度算法的调度思想是让所有 runnable 态的进程分时轮流使用 CPU 时间。Round Robin 调度器维护当前 runnable进程的有序运行队列。当前进程的时间片用完之后,调度器将当前进程放置到运行队列的尾部,再从其头部取出进程进行调度。

Stride Scheduling

具体看一下Stride Scheduling

1、为每个runnable的进程设置一个当前状态stride,表示该进程当前的调度权。另外定义其对应的pass值,表示对应进程在调度后,stride 需要进行的累加值。

2、每次需要调度时,从当前 runnable 态的进程中选择 stride最小的进程调度。对于获得调度的进程P,将对应的stride加上其对应的步长pass(只与进程的优先权有关系)。

3、在一段固定的时间之后,回到步骤2,重新调度当前stride最小的进程

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;
}

首先是队列初始化函数

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));
#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_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));
#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 ++;
}
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 --;
}
static struct proc_struct *
stride_pick_next(struct run_queue *rq) {
/* LAB6: YOUR CODE */
#if USE_SKEW_HEAP
if (rq->lab6_run_pool == NULL) return NULL;
struct proc_struct *p = le2proc(rq->lab6_run_pool, lab6_run_pool);
#else
list_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;
else p->lab6_stride += BIG_STRIDE / p->lab6_priority;
return p;
}

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

static void
stride_proc_tick(struct run_queue *rq, struct proc_struct *proc) {
/* LAB6: YOUR CODE */
if (proc->time_slice > 0)
{
proc->time_slice --;
}
if (proc->time_slice == 0)
{
proc->need_resched = 1;
}
}

最后是时间片函数stride_proc_tick,主要工作是检测当前进程是否已用完分配的时间片。

相对于这两个算法我觉得更重要的是明白进程的调度时机

  1. 时钟中断处理函数检测到时间片到期了
  2. 发生阻塞或者睡眠等情况

Lab6:进程的调度的更多相关文章

  1. 实验二 用C语言表示进程的调度

    实验二 一. 实验目的 通过模拟进程的调度,进一步了解进程的调度的具体过程. 二. 实验内容和要求 1.进程PCB的结构体定义 2.定义队列 3.输入进程序列 4.排序(按到位时间) 5.输出进程运行 ...

  2. RHCA学习笔记:RH442-Unit8进程与调度

      UNIT 8  Processes and the Scheduler 进程与调度 学习目标 A. CPU cache 与Service time之间的关系 B. 分析应用程序使用CPU cach ...

  3. Linux进程组调度机制分析【转】

    转自:http://oenhan.com/task-group-sched 又碰到一个神奇的进程调度问题,在系统重启过程中,发现系统挂住了,过了30s后才重新复位,真正系统复位的原因是硬件看门狗重启的 ...

  4. 《linux内核设计与实现》阅读笔记-进程与调度

      一.进程 process: executing program code(text section) data section containing global variables open f ...

  5. 分析Linux内核中进程的调度(时间片轮转)-《Linux内核分析》Week2作业

    1.环境的搭建: 这个可以参考孟宁老师的github:mykernel,这里不再进行赘述.主要是就是下载Linux3.9的代码,然后安装孟宁老师编写的patch,最后进行编译. 2.代码的解读 课上的 ...

  6. Linux进程核心调度器之主调度器schedule--Linux进程的管理与调度(十九)

    主调度器 在内核中的许多地方, 如果要将CPU分配给与当前活动进程不同的另一个进程, 都会直接调用主调度器函数schedule, 从系统调用返回后, 内核也会检查当前进程是否设置了重调度标志TLF_N ...

  7. Golang进程权限调度包runtime三大函数Gosched、Goexit、GOMAXPROCS

    转自:https://www.cnblogs.com/wt645631686/p/9656046.html runtime.Gosched(),用于让出CPU时间片,让出当前goroutine的执行权 ...

  8. linux进程、调度、线程、进程上下文等几点理解

    1.信号来自进程或内核 2.线程共享进程的代码空间和数据空间(全局变量或静态变量),文件描述符,信号,以及malloc分配的内存,每个线程拥有独立的栈空间和程序计数器,在创建线程时,调用pthread ...

  9. 《Linux内核设计与实现》读书笔记(四)- 进程的调度

    主要内容: 什么是调度 调度实现原理 Linux上调度实现的方法 调度相关的系统调用 1. 什么是调度 现在的操作系统都是多任务的,为了能让更多的任务能同时在系统上更好的运行,需要一个管理程序来管理计 ...

随机推荐

  1. (day33)数据库

    目录 1. 数据库是什么 2. 为什么使用数据库 3. 数据库的分类 1. 关系型数据库 2. 非关系型数据库 4. mysql的架构 5. mysql的安装 1. windows的安装 2. win ...

  2. Java开发中的23中设计模式详解(一)工厂方法模式和抽象工厂模式

    一.设计模式的分类 总体来说设计模式分为三大类: 创建型模式,共五种:工厂方法模式.抽象工厂模式.单例模式.建造者模式.原型模式. 结构型模式,共七种:适配器模式.装饰器模式.代理模式.外观模式.桥接 ...

  3. textarea如何实现高度自适应(一)

    转自轩枫阁 - http://www.xuanfengge.com/textarea-on-how-to-achieve-a-high-degree-of-adaptive.html 方法一:div模 ...

  4. VS2008给图标工具栏-状态栏添加响应函数

    1.在对话框的:OnInitDialog()函数中添加以下红色代码: BOOL CGSM_MessageDlg::OnInitDialog() { CDialog::OnInitDialog(); / ...

  5. 我的【Java】面试日记

    背景 在老东家五年了,总共工作整七年,经历两家公司.2019-10-31日离职.公司规模较小,项目压力不大,非985/211毕业,统招本科,计算机专业.目标:中大型公司,最好是大厂,嘿嘿,不过不抱希望 ...

  6. CSPS模拟 74

    T1 贪心,如果用set考虑一下multi. T2 难道是我的疑问都太过sb? 从来没人愿意认真思考一下我的问题. 更好,思考量这东西本该我自己来补. 设$dp[i][j]$为i个点的森林,j个点在特 ...

  7. Linux下Redis安装与配置

    http://www.cnblogs.com/_popc/p/3684835.html

  8. 详谈Generator

    Generator究竟有什么样的作用呢???? 他是ES6提出的一个解决异步问题方案 先看一段代码, 感受一下generator函数和普通函数的区别 function* test(){ yield 2 ...

  9. 原生JS封装_new函数,实现new关键字的功能

    1.前言 众所周知:没有对象怎么办?那就new一个! 那么在JS中,当我们new一个对象的时候,这个new关键字内部都干了什么呢? 现在我们就来剖析一下原生JS中new关键字内部的工作原理. 2.原生 ...

  10. go中的数据结构切片-slice

    1.部分基本类型 go中的类型与c的相似,常用类型有一个特例:byte类型,即字节类型,长度为,默认值是0: bytes = []btye{'h', 'e', 'l', 'l', 'o'} 变量byt ...