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. 发生阻塞或者睡眠等情况

最新文章

  1. IE10(去掉文本框的X)
  2. js 的一些知识 摘自http://img0.pconline.com.cn/Pc_intranet/1105/13/313647_7.pdf
  3. UVa 11774 (置换 找规律) Doom's Day
  4. [转]IOS, xib和storyboard的混用
  5. error when loading the sdk 发现了元素 d:skin 开头无效内容
  6. JQuery弹出层,点击按钮后弹出遮罩层,有关闭按钮
  7. Sizzle一步步实现所有功能(层级选择)
  8. Jacobi symbol(裸雅可比符号)
  9. mongodb 数据备份与恢复
  10. 8 个最好的 jQuery 树形 Tree 插件
  11. JDK源码分析(12)之 ConcurrentHashMap 详解
  12. web api 安全
  13. bootstrap实现列的拖动
  14. Confluence 6 用户目录图例 - 连接 Jira
  15. POJ3635 Full Tank?【Dijkstra+DP】
  16. Dom4j用Xpath获取节点——(六)
  17. SharePoint 2010 使用Install-SPSolution部署wsp包状态一直是”正在部署”
  18. 第三届CCF软件能力认证
  19. Kafka单机环境的部署
  20. python 判断一个对象的变量类型

热门文章

  1. Git的安装及布置
  2. SpringCloud之Nacos服务发现(十七)
  3. Okhttp 请求流程梳理
  4. ArcGIS Engine连接ArcSDE SQL Server(获得所有SDE图层)
  5. [2018-01-12] laravel--ORM
  6. (七)golang-变量之基本数据类型(看这篇就够了)
  7. JS中获取元素属性的逆天大法
  8. 爬虫学习--Urllib库基本使用 Day1
  9. Mybaits 源码解析 (十二)----- Mybatis的事务如何被Spring管理?Mybatis和Spring事务中用的Connection是同一个吗?
  10. 易初大数据——2019年10月17日 王庆超 spss