第四章 进程调度

调度程序负责决定将哪个进程投入运行,何时运行以及运行多长时间。进程调度程序可看做在可运行态进程之间分配有限的处理器时间资源的内核子系统。只有通过调度程序的合理调度,系统资源才能最大限度地发挥作用,多进程才会有并发执行的效果。

4.1 多任务

1.多任务操作系统就是能同时并发地交互执行多个进程的操作系统。无论在单处理或者多处理器机器上,多任务操作系统都能使多个进程处于拥塞或者睡眠状态,也就是实际上不被投入执行,直到工作确实就绪。

2.多任务系统可以划分为两类:非抢占式多任务和抢占式多任务。

3.Linux提供了抢占式的多任务模式。进程在被抢占之前能够运行的时间是预先设置好的,叫进程的时间片。时间片实际上就是分配给每个可运行进程的处理器时间段。

4.3 策略

策略决定调度程序在何时让什么进程运行。

4.3.1 I/O消耗型和处理器

1.进程可以被分为I/O消耗型(进程的大部分时间用来提交I/O请求或是等待I/O请求)和处理器消耗型(把时间用在执行代码上)。

2.划分方式不是绝对的,进程可以同时展现这两种行为,比如X Window服务器。

3.调度策略通常要在两个矛盾目标中间寻找平衡:进程响应迅速(响应时间短)和最大系统利用率(高吞吐量)。

4.3.2 进程优先级

1.调度算法中最基本的一类就是基于优先级的调度。这是一种根据进程的价值和其对处理器时间的需求来对进程分级的想法。通常做法是优先级高的进程先运行,低的后运行,相同优先级的进程按轮转方式进行调度(一个接一个,重复进行)。调度程序总是选择时间片未用尽而且优先级最高的进程运行。用户和系统都可以通过设置进程的优先级来影响系统的调度。

2.优先级分为两类

  • nice值(从-20——+19):默认值为0;数值越大意味着优先级越低;可以通过 ps-el查看系统进程列表并找到NI标记列对应的优先级
  • 实时优先级(从0——99):越高的实时优先级级数意味着进程优先级越高
  • 二者互不交互

4.3.3.时间片

1.时间片表示进程在被抢占之前所能够持续运行的时间;调度策略必须确定一个默认的时间片;

2.Linux的CFS调度器并没有直接划分时间片到进程,而是将处理器的使用比例划分给了进程。也就是说,其抢占时机取决于新的可执行程序消耗了多少处理器使用比,如果消耗的使用比比当前进程小,则新进程立即投入运行抢占当前进程。

4.4 Linux调度算法

4.4.1 调度器类

  1. Linux调度器是以模块方式提供的(也就是调度器类),目的是允许不同类型的进程可以有针对性地选择调度算法
  2. 调度器类允许多种不同的可动态添加的调度算法并存,调度属于自己范畴的进程;
  3. 调度器代码会按照优先级顺序遍历调度类,拥有一个可执行进程的最高优先级的调度器类胜出,去选择下面要执行的那一个程序;

4.4.2 Unix中系统调度问题

  1. 将nice值映射到时间片的话,就必须将nice值对应到处理器的绝对时间;这样会导致进程切换无法最优进行;
  2. 如果使用相对nice值,所带来的效果将会极大取决于其nice的初始值;
  3. 如果执行nice值到时间片的映射,时间片极大受制于定时器。

4.4.3 公平调度

  1. CFS基于一个简单的理念:进程调度的效果应当如同系统具备一个理想中的完美任务处理器。CFS的做法如下:

    • 允许每个进程运行一段时间、循环轮转、选择运行最少的进程作为下一个运行进程;
    • nice值作为进程获得的处理器运行比的权重(而不是完全由nice决定时间片);
    • 每个进程都按照其权重在全部的可运行进程中所占的比例对应的“时间片”来运行

【所谓“鱼与熊掌不可得兼”即如此——越小的调度周期就会表现出越好的交互性,也更接近于“同时完成多任务”这一孜孜追求的目标;然而系统必须承受更高的切换代价和更差的系统吞吐量——甚至将绝大多数精力耗费在这种来回倒腾上】

4.5 Linux调度的实现

4.5.1 时间记账

  • 所有的调度器都必须对进程的运行时间做记账;
  • CFS使用调度器实体结构来追踪运行记账

4.5.2 虚拟实时

  • vrntime变量【也就是在上面所说的实体结构中】存放虚拟运行时间。虚拟时间以ns为单位,和节拍定时器无关;
  • update_curr()函数实现了记账功能;计算了当前进程的执行时间并将其存放在data_exec中;然后将运行时间传递给了_update_curr(),由后者再根据当前可运行进程总数对运行时间进行计算,最终确定上述的权重值与当前运行进程的vrntime。

4.5.3 进程选择

  1. CFS算法核心:选择具有最小vrntime的任务
  2. 具体做法:利用红黑树rbtree(以节点形式存储数据的二叉树)
  3. 举例:
    • 选择下一个任务:从根节点中序遍历二叉树,一直到叶子节点(也就是vrntime最小的进程);
    • 向树中加入进程:在进程变为可执行状态或者通过fork()调用第一次创建进程;
    • 从树中删除进程:发生在进程阻塞或者终止的时候

【由此我们可以看到,二叉树中存储的全部是可执行进程】

4.5.4 进程调度入口

  1. 进程调度的主要入口点是函数schedule(),定义在kernel/sched.c中;这正是内和其他部分用于调度进程调度器的入口
  2. 这一函数最重要的工作就是调用pick_next_state(),依次检查每一个调度类,并从最高优先级的调度类中,选择最高优先级进程

4.5.5 和唤醒

  1. 进程休眠一定是为了等待一些事件

    • 进程把自己标记成休眠状态,从可执行红黑树中移除;
    • 放入等待队列——由等待某些时间发生的进程组成的链表,内核用wake_queue_head_t来代表等待队列
  2. 唤醒操作由函数wake_up()进行
    • 它会调用函数try_to _wake_up()将进程设置为TASK_RUNNING状态,调用enqueue_task()将进程放入红黑树中
    • 当然,也存在虚假唤醒进程的状态

4.6 抢占和上下文切换

4.6.1 用户抢占

上下文切换由定义在kernel/sched.c中的context_switch()函数负责,每当一个新的进程被选出来准备运行的时候,schedule()就会调用该函数:

  • 调用switch_mm(),负责把虚拟内存从上一个进程映射切换到新的进程中;
  • 调用switch_to(),负责从上一个进程的处理器状态切换到新进程的处理器状态

4.6.2  内核抢占

  1. 只要没有锁,内核就可以进程抢占;
  2. 为了支持抢占,每个进程的thread_info都加入了preempt_count计数器(初值为0,每当使用锁的时候就加1,释放锁的时候数值减1),当数值为0的时候,内核就可以抢占
  3. 内核抢占发生在:
    • 中断处理程序正在执行且返回内核空间之前;
    • 内核代码再一次具有可抢占性的时候;
    • 内核中的任务显式地调用schedule函数

4.7 实时策略调度

1.linux 提供两种实时调度策略SCHED_FIFO和SCHED_RR。具体的实现定义在kernel/sched_rt.c中。

  两种实时算法实现的都是静态优先级。

2.Linux的实时调度算法提供一种软实时工作方式,软实时的含义是:内核调度进程,尽力使进程在他的限定时间到来前运行,但内核不保证总能满足这些进程的要求。硬实时系统则可以满足任何调度的要求。

3.实时优先级范围从0到MAX_RT_PRIO减1。MAX_RT_PRIO默认为100

4.8 与调度相关的系统调用

  1. linux 提供一个系统调用族,用于管理与调度程序相关参数。这些系统调用可以用来操作和处理进程优先级、调度策略和处理器绑定,还提供了显式地将处理器交给其他进程的机制。

4.8.1 与调度策略和优先级相关的系统调用

1.Sched_setscheduler():设置进程的调度策略和实时优先级

2.Sched_getscheduler():获取进程的调度策略和实时优先级

最重要的工作在于读取或改写进程tast_struct的policy和rt_priority的值。

3.Sched_setparam():设置进程的实时优先级

4.Sched_getparam():获取进程的实时优先级

4.8.2 与调度策略和优先级相关的系统调用

用户可以通过sched_setaffinity()设置不同的一个或者几个位组合的位掩码,而调用sched_getaffinity()则返回当前的cpus_allowed位掩码。

4.8.3 放弃处理器时间

Linux通过sched_yield()系统调用,提供一种让进程显式地将处理器时间让给其他等待执行进程的机制。内核代码为了方便,可直接调用yield(),先确定给定进程确实处于可执行状态,然后再调用sched_yield()。用户空间的应用程序直接使用sched_yield()系统调用就可以了。

Linux内核分析 读书笔记 (第四章)的更多相关文章

  1. Linux内核分析 读书笔记 (第一章、第二章)

    第一章 Linux内核简介 1.1 Unix的历史 Unix很简洁,仅仅提供几百个系统调用并且有一个非常明确的设计目的. 在Unix中,所有东西都被当做文件,这种抽象使对数据和对设备的操作是通过一套相 ...

  2. 20135239 益西拉姆 linux内核分析 读书笔记之第四章

    chapter 4 进程调度 4.1 多任务 多任务操作系统就是能同时并发的交互执行多个进程的操作系统. 多任务系统可以划分为两类: - 非抢占式多任务: - 进程会一直执行直到自己主动停止运行(这一 ...

  3. 《Linux内核分析》之第四章读书笔记

    4.1多任务 多任务操作系统:同时并发地交互执行多个进程的操作系统 多任务操作系统会使多个进程处于堵塞或者睡眠状态.这些任务尽管位于内存,但是并不处于可运行状态.这些进程利用内核堵塞自己,直到某一事件 ...

  4. Linux内核分析 读书笔记 (第七章)

    第七章 链接 1.链接是将各种代码和数据部分收集起来并组合成为一个单一文件的过程,这个文件可被加载(或被拷贝)到存储器并执行. 2.链接可以执行于编译时,也就是在源代码被翻译成机器代码时:也可以执行于 ...

  5. Linux内核分析 读书笔记 (第三章)

    第三章 进程管理 3.1 进程 1.进程: 进程就是处于执行期的程序. 进程就是正在执行的程序代码的实时结果. 进程是处于执行期的程序以及相关的资源的总称. 进程包括代码段和其他资源. 2.线程:执行 ...

  6. Linux内核分析 读书笔记 (第五章)

    第五章 系统调用 5.1 与内核通信 1.调用在用户空间进程和硬件设备之间添加了一个中间层.该层主要作用有三个: 为用户空间提供了硬件的抽象接口. 系统调用保证了系统的稳定和安全. 实现多任务和虚拟内 ...

  7. 《深入理解Linux内核》阅读笔记 --- 第四章 中断和异常

    1.中断的作用:中断信号提供了一种方式,使处理器转而去运行正常控制流之外的代码.当一个中断信号到达时,CPU必须停止它当前所做的事,并切换到一个新的活动.为了做到这一点,就要在内核态堆栈保存程序计数器 ...

  8. Linux内核分析 读书笔记 (第十八章)

    第十八章 调试 18.1 准备开始 1. 需要的只是: 一个bug 一个藏匿bug的内核版本 相关内核代码的知识和运气 2. 在跟踪bug的时候,掌握的信息越多越好. 18.2 内核中的bug 1.  ...

  9. 《Linux课本》读书笔记 第四章

随机推荐

  1. leetcode 395. Longest Substring with At Least K Repeating Characters(高质量题)

    只能说还是太菜,抄的网上大神的做法: idea: mask 的每一位代表该位字母够不够k次,够k次为0,不够为1 对于每一位将其视为起点,遍历至末尾,找到其最大满足子串T的下标max_idx,之后从m ...

  2. 通过css3,实现加载转动贝塞尔曲线动画

    参考博客:http://blog.jobbole.com/94966/ css代码: .loading { position : relative; display : inline-block; w ...

  3. GUI_文件管理器(练习)

    实现想windows下的文件管理器(主要是监听器里的方法,showDir()写法) package com.mywindow.test; import java.awt.event.ActionEve ...

  4. 【BZOJ3529】数表

    数表 Description 有一张 n*m 的数表,其第i行第j列(1<=i<=n,1<=j<=m)的数值为能同时整除 i和j的所有自然数之和.给定a,计算数表中不大于a的数 ...

  5. 在H5页面内通过地址调起高德地图实现导航

    项目中用到的一个功能是要通过点击地址来实现打开地图app实现地址导航. 如下图: 实现思路就是在H5页面内通过点击marker图标然后进行当前位置与页面上地址的路程规划与导航. 由于项目中用到的是高德 ...

  6. 使用ElasticSearch实现搜索时即时提示与全文搜索功能

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  7. Python调用WIN10语音交互+识别+控制+自定义对话

    1 安装库文件 2修改两个地方 最简单的 # 将输入文字转化为语音信号输出 import speech while True: speech.say("请输入:") str = i ...

  8. Luogu3959 NOIP2017 宝藏 状压DP

    题目传送门:https://www.luogu.org/problemnew/show/P3959 题意:给出一个有$N$个点的图,求其中的一个生成树(指定一个点为根),使得$\sum\limits_ ...

  9. vue 动态加载组建

    <component :is="comp1"></component> data () { return { comp1:'', } } require.e ...

  10. WPF中, 启用添加到RichTextBox中的控件

    原文:WPF中, 启用添加到RichTextBox中的控件   WPF中, 启用添加到RichTextBox中的控件                                           ...