6.1 进程调度概念

进程调度

在合适的时候以一定策略选择一个就绪进程运行

进程调度的目标

  1. 响应速度尽可能快
  2. 进程处理的时间尽可能短
  3. 系统吞吐量尽可能大
  4. 资源利用率尽可能高
  5. 对所有进程要公平
  6. 避免饥饿
  7. 避免死锁

上述部分原则之间存在自相矛盾

进程调度的目标(两个可以量化的衡量指标)

周转时间/平均周转时间

周转时间:进程提交给计算机到最终完成花费的时间(\(t\))

\(t_s\):进程的提交时间(start)

\(t_c\):进程的完成时间(complete)

\[t=t_c-t_s
\]

意义:说明进程在系统中停留时间长短

平均周转时间:

\[t=\frac {t_1+t_2+\cdots + t_n} {n}
\]

意义:平均周转时间越短,意味着这些进程在系统内停留的时间越短,因而系统的吞吐量也就越大,资源利用率也越高

带权周转时间/平均带权周转时间

带权周转时间:

\(t\):进程的周转时间

\(t_r\):进程的运行时间(run)

\[w=\frac t {t_r}
\]

意义:进程在系统相对停留时间、

平均带权周转时间:

\[w=\frac {w_1+w_2+\cdots+w_n} {n}
\]

6.2 典型调度算法

1. 先来先服务调度(First Come First Serve)

算法

按照作业进入系统的时间先后来挑选作业,先进入系统的作业优先被运行。

特点

  • 容易实现,效率不高
  • 只考虑作业的等候时间,而没考虑运行时间的长短。因此一个晚来但是很短的作业可能需要等待很长时间才能被运行,因为本算法不利于短作业

2. 短作业优先调度算法(Short Job First)

算法

参考运行时间,选取运行时间最短的作业投入运行

特点

  • 易于实现,效率不高
  • 忽略了作业等待时间,一个早来但是很长的作业将会在很长时间得不到调度,易出现资源“饥饿”的现象。

3. 响应比高者优先调度算法

响应比定义

作业的响应时间和运行时间的比值

\[响应比=\frac {响应时间}{运行时间}=\frac {等待时间+运行时间}{运行时间}=1+\frac{等待时间}{运行时间}
\]

算法

计算每个作业的响应比,选择响应比最高的作业优先投入运行。

特点

  • \(响应比=1+\frac{等待时间}{运行时间}\)
  • 如果作业的等待时间相同,则运行时间越短的作业,其响应比越高,因此越容易被调度,因而有利于短作业。
  • 如果作业的运行时间相同,则等待时间越长的作业,其响应比越高,因此越容易被调度。因而有利于等候长的作业。
  • 对于运行时间长的作业,其优先级可以随等候时间的增加而提高,当其等待足够久的时候,也有可能获得CPU。

4. 优先数调度算法

算法

  • 根据进程优先数,把CPU分配给最高的进程
  • 进程优先数=静态优先数+动态优先数

静态优先数

进程创建时确立,在整个进程运行期间不再改变

动态优先数

动态优先数再进程运行期间可以改变

静态优先数的确立

  • 基于进程所需资源的多少
  • 基于程序运行时间的长短
  • 基于进程的类型(IO/CPU,前台/后台,核心/用户)

动态优先数的确立

  • 当使用CPU超过一定时常时
  • 当进行I/O操作后
  • 当进程等待超过一定时长时

5. 循环轮转调度法(Round-Robin)

概念

  • 把所有就绪进程按先进先出的原则拍成队列。新来的进程加到队列末尾。
  • 进程以时间片q为单位轮流使用CPU。刚刚运行一个时间片的进程排到队列末尾,等候下一轮运行。
  • 队列逻辑上是环形的。

优点:

  • 公平性:每个就绪进程有平等机会获得CPU
  • 交互性:每个进程等待\((N-1)\cdot q\)的时间就可以重新获得CPU

时间片q的大小

  1. 如果q太大

    • 交互性差
    • 甚至退化为FCFS调度算法
  2. 如果q太小
    • 进程切换频繁,系统开销增加

改进

  • 时间片的大小可变
  • 组织多个就绪队列

6.3 Linux进程调度

Linux进程类型

普通进程

  • 采用动态优先级来调度
  • 调度程序周期性地修改优先级(避免饥饿)

实时进程

  • 采用静态优先级来调度
  • 由用户预先指定,以后不会改变

Linux进程的优先级

静态优先级

  • 进程创建时指定或由用户修改

动态优先级

  • 在进程运行期间可以按照调度策略改变
  • 非实时进程采用动态优先级,由调度程序计算
  • 只要进程占用CPU,优先级就随着时间流逝而不断减小
  • task_struct的counter表示动态优先级

调度策略(结合task_struct)

task_struct->policy指明进程调度策略

实时进程

  • SCHED_FIFO(先进先出)

    • 当前实时进程一直占用CPU直至退出阻塞被抢占
    • 阻塞后再就绪时被添加到同优先级队列的末尾
  • SCHED_RR(时间片轮转)
    • 与其他实时进程以Round-Robin方式共同使用CPU
    • 确保同优先级的多个进程能共享CPU

非实时进程

  • SCHED_OTHER(动态优先级)
  • counter成员表示动态优先级

调度策略的改变

  • 系统调用sched_setscheduler()来改变调度策略
  • 实时进程的子孙进程也是实时进程

进程调度的依据

task_struct
policy 进程的调度策略,用来区分实时进程和普通进程;SCHED_OTHER(0)||SCHED_FIFO(1)||SCHED_RR(2)
priority 进程(包括实时和普通)的静态优先级
rt_priority 实时进程特有的优先级:rt_priority+1000
counter 进程能够连续运行的时间

动态优先级与counter

counter值的含义

  • 进程能连续运行的时间,单位是时钟滴答tick

    时钟中断周期tick为10ms;若counter=60,则能连续运行600ms。

  • 较高优先级的进程一般counter较大。

  • 一般把counter看作动态优先级。

counter的初值与priority有关

  • 普通进程创建时counter的初值为 priority的值。

counter的改变

  • 时钟中断tick时,当前进程的counter减1,直到为0被阻塞。

子进程新建时的counter

新建子进程counter从父进程的时间片counter中继承一半

p->counter = (current->counter + 1) >> 1;
current->counter >>= 1;

防止用户无限制地创建后代进程而长期占有CPU资源

调度时机

  1. 中断处理过程中直接调用schedule()

    • 时钟中断、I/O中断、系统调用和异常
    • 内核被动调度的情形
  2. 中断处理过程返回用户态时直接调用schedule()

    • 必须根据need_resched标记
  3. 内核线程可直接调用schedule()进行进程切换

    • 内核主动调度的情形
  4. 用户态进程只能通过陷入内核后在中断处理过程中被动调度

    • 必须根据need_resched标记

进程切换

概念

  • 内核挂起当前CPU上的进程并恢复之前挂起的某个进程
  • 任务切换、上下文切换

与中断上下文的切换有去边

  • 中断前后在同一进程上下文中,只是用户态转向内核态执行

进程上下文包含了进程执行需要的所有信息

  • 用户地址空间:包括程序代码,数据,用户堆栈等
  • 控制信息:进程描述符,内核堆栈等
  • 硬件上下文(注意中断也要保存硬件上下文,只是保存的方法不同)

进程调度和切换的流程

schedule()函数

  1. 选择新进程

    next = pick_next_task(rq, prev); //进程调度算法
  2. 调用宏context_switch (rq, prev, next)切换进程上下文

    • prev: 当前进程,next:被调度的新进程
    • 调用switch_to(prev, next)切换上下文

两个进程A,B切换的基本过程

  1. 正在运行用户态进程A

  2. 发生中断(譬如时钟中断)

    • 保存current当前进程的cs:eip/esp/eflags到内核堆栈
    • 从内核堆栈装入ISR中断服务例程的cs:eip和ss:esp
  3. SAVE_ALL //保存现场,已进入内核中断处理过程

  4. 中断处理过程中或中断返回前调用了 schedule()

    • 其中的switch_to做了进程上下文切换
  5. 运行用户态进程B (B曾经通过以上步骤被切换出去过)

  6. RESTORE_ALL //恢复现场

  7. iret //中断返回 pop cs:eip/ss:esp/eflags

  8. 继续运行用户态进程B

【av68676164(p38-p40)】进程调度的更多相关文章

  1. 遗传算法的C语言实现(二)-----以求解TSP问题为例

    上一次我们使用遗传算法求解了一个较为复杂的多元非线性函数的极值问题,也基本了解了遗传算法的实现基本步骤.这一次,我再以经典的TSP问题为例,更加深入地说明遗传算法中选择.交叉.变异等核心步骤的实现.而 ...

  2. 遗传算法的C语言实现(二)

    上一次我们使用遗传算法求解了一个较为复杂的多元非线性函数的极值问题,也基本了解了遗传算法的实现基本步骤.这一次,我再以经典的TSP问题为例,更加深入地说明遗传算法中选择.交叉.变异等核心步骤的实现.而 ...

  3. 【av68676164(p15-p17)】进程概念

    from av68676164 4.1.1 进程的基本概念 程序运行在并发环境中的问题 运行过程不确定 结果不可再现(程序运行被干扰) 解决方案:对运行过程施加约束 新的概念:进程 描述和管理程序的& ...

  4. 《Linux内核设计与实现》读书笔记 第四章 进程调度

    第四章进程调度 进程调度程序可看做在可运行太进程之间分配有限的处理器时间资源的内核子系统.调度程序是多任务操作系统的基础.通过调度程序的合理调度,系统资源才能最大限度地发挥作用,多进程才会有并发执行的 ...

  5. Linux2.6内核--进程调度理论

    从1991年Linux的第1版到后来的2.4内核系列,Linux的调度程序都相当简陋,设计近乎原始,见0.11版内核进程调度.当然它很容易理解,但是它在众多可运行进程或者多处理器的环境下都难以胜任. ...

  6. Linux0.11内核--进程调度分析之2.调度

    [版权所有,转载请注明出处.出处:http://www.cnblogs.com/joey-hua/p/5596830.html ] 上一篇说到进程调度归根结底是调用timer_interrupt函数, ...

  7. Linux0.11内核--进程调度分析之1.初始化

    [版权所有,转载请注明出处.出处:http://www.cnblogs.com/joey-hua/p/5596746.html ] 首先看main.c里的初始化函数main函数里面有个函数是对进程调度 ...

  8. linux 内核学习之八 进程调度过程分析

    一  关于进程的补充 进程调度的时机 中断处理过程(包括时钟中断.I/O中断.系统调用和异常)中,直接调用schedule(),或者返回用户态时根据need_resched标记调用schedule() ...

  9. Linux内核分析——理解进程调度时机跟踪分析进程调度与进程切换的过程

    20135125陈智威 +原创作品转载请注明出处 +<Linux内核分析>MOOC课程http://mooc.study.163.com/course/USTC-1000029000 实验 ...

随机推荐

  1. java 面向对象(三十三):泛型二 泛型在集合中的使用

    1. 在集合中使用泛型之前的例子 @Test public void test1(){ ArrayList list = new ArrayList(); //需求:存放学生的成绩 list.add( ...

  2. 数据可视化之powerBI技巧(二十三)Power BI可视化技巧,使用DAX自定义时间轴

    ​按照自然日历来展现疫情数据时,是这样的效果, 由于各个国家的疫情爆发时间不一致,按自然日期坐标轴很难比较各个国家的蔓延速度. 如果各个国家都从蔓延日开始统计,展示之后每日的确诊人数,就是同样的时间轴 ...

  3. 数据可视化之DAX篇(九) 关于DAX中的VAR,你应该避免的一个常见错误

    https://zhuanlan.zhihu.com/p/67803111 本文源于微博上一位朋友的问题,在计算同比增长率时,以下两种DAX代码有什么不同? -------------------- ...

  4. Jmeter系列(45)- 详解 Jmeter 跨线程组取参数值的方法,免代码!

    如果你想从头学习Jmeter,可以看看这个系列的文章哦 https://www.cnblogs.com/poloyy/category/1746599.html 前言 用过 Jmeter 的同学应该都 ...

  5. sanri-tools-maven 企业软件开发工具集

    9420 开发工具包 sanri-tools-maven 是一个开源的用于企业开发的工具包,重点想解决项目开发中一些比较麻烦的问题 根据表和模板生成相应代码:一些身份证,企业代码,车架号的验证与生成: ...

  6. UnsupportedClassVersionError的错误处理

    造成这种错误的原因是支持Tomcat运行的JDK版本与支持application运行的JDK版本不一致导致的. 解决办法: 将JDK版本改成一致. 步骤 1.Window ——> Prefere ...

  7. async基本使用

    async函数在使用上很简单,我们来看一下下面的例子 async function add(a,b){ return a+b } add(1,2).then((res) =>{ consoel. ...

  8. Redis(一)简介及安装、测试

    一.Redis简介: 关于关系型数据库和nosql数据库 关系型数据库是基于关系表的数据库,最终会将数据持久化到磁盘上,而nosql数据 库是基于特殊的结构,并将数据存储到内存的数据库.从性能上而言, ...

  9. 如何使用Istio 1.6管理多集群中的微服务?

    假如你正在一家典型的企业里工作,需要与多个团队一起工作,并为客户提供一个独立的软件,组成一个应用程序.你的团队遵循微服务架构,并拥有由多个Kubernetes集群组成的广泛基础设施. 由于微服务分布在 ...

  10. Python os.removedirs() 方法

    概述 os.removedirs() 方法用于递归删除目录.像rmdir(), 如果子文件夹成功删除, removedirs()才尝试它们的父文件夹,直到抛出一个error(它基本上被忽略,因为它一般 ...