Lab6:进程的调度
CPU调度
从就绪队列中挑选下一个占用CPU运行的进程,从多个可用CPU中挑选就绪进程可使用的CPU资源
调度策略
比较调度算法的准则
- CPU使用率
- 吞吐量
- 周转时间
- 就绪等待时间
- 响应时间
吞吐量与延迟
低延迟:喝水的时候想要一打开水龙头水就流出来
高带宽:给游泳池充水时希望从水龙头里同时流出大量的水,并且不介意是否存在延迟
处理机调度策略的响应时间目标
- 减少响应时间
- 减少平均响应时间的波动
- 增加吞吐量
- 减少等待时间
调度算法
先来先服务算法(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,主要工作是检测当前进程是否已用完分配的时间片。
相对于这两个算法我觉得更重要的是明白进程的调度时机
- 时钟中断处理函数检测到时间片到期了
- 发生阻塞或者睡眠等情况
Lab6:进程的调度的更多相关文章
- 实验二 用C语言表示进程的调度
实验二 一. 实验目的 通过模拟进程的调度,进一步了解进程的调度的具体过程. 二. 实验内容和要求 1.进程PCB的结构体定义 2.定义队列 3.输入进程序列 4.排序(按到位时间) 5.输出进程运行 ...
- RHCA学习笔记:RH442-Unit8进程与调度
UNIT 8 Processes and the Scheduler 进程与调度 学习目标 A. CPU cache 与Service time之间的关系 B. 分析应用程序使用CPU cach ...
- Linux进程组调度机制分析【转】
转自:http://oenhan.com/task-group-sched 又碰到一个神奇的进程调度问题,在系统重启过程中,发现系统挂住了,过了30s后才重新复位,真正系统复位的原因是硬件看门狗重启的 ...
- 《linux内核设计与实现》阅读笔记-进程与调度
一.进程 process: executing program code(text section) data section containing global variables open f ...
- 分析Linux内核中进程的调度(时间片轮转)-《Linux内核分析》Week2作业
1.环境的搭建: 这个可以参考孟宁老师的github:mykernel,这里不再进行赘述.主要是就是下载Linux3.9的代码,然后安装孟宁老师编写的patch,最后进行编译. 2.代码的解读 课上的 ...
- Linux进程核心调度器之主调度器schedule--Linux进程的管理与调度(十九)
主调度器 在内核中的许多地方, 如果要将CPU分配给与当前活动进程不同的另一个进程, 都会直接调用主调度器函数schedule, 从系统调用返回后, 内核也会检查当前进程是否设置了重调度标志TLF_N ...
- Golang进程权限调度包runtime三大函数Gosched、Goexit、GOMAXPROCS
转自:https://www.cnblogs.com/wt645631686/p/9656046.html runtime.Gosched(),用于让出CPU时间片,让出当前goroutine的执行权 ...
- linux进程、调度、线程、进程上下文等几点理解
1.信号来自进程或内核 2.线程共享进程的代码空间和数据空间(全局变量或静态变量),文件描述符,信号,以及malloc分配的内存,每个线程拥有独立的栈空间和程序计数器,在创建线程时,调用pthread ...
- 《Linux内核设计与实现》读书笔记(四)- 进程的调度
主要内容: 什么是调度 调度实现原理 Linux上调度实现的方法 调度相关的系统调用 1. 什么是调度 现在的操作系统都是多任务的,为了能让更多的任务能同时在系统上更好的运行,需要一个管理程序来管理计 ...
随机推荐
- (day33)数据库
目录 1. 数据库是什么 2. 为什么使用数据库 3. 数据库的分类 1. 关系型数据库 2. 非关系型数据库 4. mysql的架构 5. mysql的安装 1. windows的安装 2. win ...
- Java开发中的23中设计模式详解(一)工厂方法模式和抽象工厂模式
一.设计模式的分类 总体来说设计模式分为三大类: 创建型模式,共五种:工厂方法模式.抽象工厂模式.单例模式.建造者模式.原型模式. 结构型模式,共七种:适配器模式.装饰器模式.代理模式.外观模式.桥接 ...
- textarea如何实现高度自适应(一)
转自轩枫阁 - http://www.xuanfengge.com/textarea-on-how-to-achieve-a-high-degree-of-adaptive.html 方法一:div模 ...
- VS2008给图标工具栏-状态栏添加响应函数
1.在对话框的:OnInitDialog()函数中添加以下红色代码: BOOL CGSM_MessageDlg::OnInitDialog() { CDialog::OnInitDialog(); / ...
- 我的【Java】面试日记
背景 在老东家五年了,总共工作整七年,经历两家公司.2019-10-31日离职.公司规模较小,项目压力不大,非985/211毕业,统招本科,计算机专业.目标:中大型公司,最好是大厂,嘿嘿,不过不抱希望 ...
- CSPS模拟 74
T1 贪心,如果用set考虑一下multi. T2 难道是我的疑问都太过sb? 从来没人愿意认真思考一下我的问题. 更好,思考量这东西本该我自己来补. 设$dp[i][j]$为i个点的森林,j个点在特 ...
- Linux下Redis安装与配置
http://www.cnblogs.com/_popc/p/3684835.html
- 详谈Generator
Generator究竟有什么样的作用呢???? 他是ES6提出的一个解决异步问题方案 先看一段代码, 感受一下generator函数和普通函数的区别 function* test(){ yield 2 ...
- 原生JS封装_new函数,实现new关键字的功能
1.前言 众所周知:没有对象怎么办?那就new一个! 那么在JS中,当我们new一个对象的时候,这个new关键字内部都干了什么呢? 现在我们就来剖析一下原生JS中new关键字内部的工作原理. 2.原生 ...
- go中的数据结构切片-slice
1.部分基本类型 go中的类型与c的相似,常用类型有一个特例:byte类型,即字节类型,长度为,默认值是0: bytes = []btye{'h', 'e', 'l', 'l', 'o'} 变量byt ...