4.1 多任务

多任务操作系统就是能同时并发地交互执行多个进程的操作系统。

多任务系统可以划分为两类:

  • 非抢占式多任务
    进程会一直执行直到自己主动停止运行
  • 抢占式多任务
    Linux/Unix使用的是抢占式的方式;强制的挂起进程的动作就叫做抢占。

像所有unix的变体和许多其他现代操作系统一样,Linux提供了抢占式的多任务模式。

进程的时间片:进程在被抢占之前能够运行的时间是预先设置好的。

4.2 Linux 的进程调度36

  • 从1991年Linux的第1版到后来的2.4内核系列,Linux的调度程序都相当简陋,设计近乎原始,当然它很容易理解,但是它在众多可运行进程或者多处理器的环境下都难以胜任,正因为如此,在Linux 2.5开发系列的内核中,调度程序做了大手术,开始采用了一种叫做O(1)调度程序的新调度程序——它是因为其算法的行为而得名的。

  • 它解决了先前版本Linux调度程序的许多不足,引入了许多强大的新特性和性能特征,这里主要要感谢静态时间片算法和针对每一处理器的运行队列,它们帮助我们摆脱了先前调度程序设计上的限制。

  • O(1)调度器虽然在拥有数以十计(不是数以百计)的多处理器的环境下尚能表现出近乎完美的性能和可扩展性,但是时间证明该调度算法对于调度那些响应时间敏感的程序却有一些先天不足,这些程序我们称其为交互进程一它无疑包括了所有需要用户交互的程序,正因为如此,O(1)调度程序虽然对于大服务器的工作负载很理想,但是在有很多交互程序要运行的桌面系统上则表现不佳,因为其缺少交互进程,自2.6内核系统开发初期,开发人员为了提高对交互程序的调度性能引入了新的进程调度算法,其中最为著名的是“反转楼梯最后期限调度算法,该算法吸取了队列理论,将公平调度的概念引入了Linux调度程序。并且最终在2.6.23内核版本中替代了O(1)调度算法,它此刻被称为“完全公平调度算法”,或者简称CFS。

4.3 策略

4.3.1 I/O消耗型和处理器消耗型的进程

调度策略通常要在两个矛盾的目标中间寻找平衡:进程响应迅速(响应时间短)和最大系统利用率(高吞吐量),为了满足上述需求,调度程序通常采用一套非常复杂的算法来决定最值得运行的进程投入运行,但是它往往并不保证低优先级进程会被公平对待,Unⅸ系统的调度程序更倾向于I/O消耗型程序,以提供更好的程序响应速度,Linux为了保证交互式应用和桌面系统的性能,所以对进程的响应做了优化(缩短响应想间)更倾向于优先调度I/O消耗型进程,虽然如此,但在下面你会看到,调度程序也并未忽略处理器消耗型的进程。

4.3.2 进程优先级

  • 调度算法中最基本的一类就是基于优先级的调度,这是一种根据进程的价值和其对处理器时间的需求来对进程分级的想法,通常做法是优先级高的进程先运行,低的后运行,相同优先级的进程按轮转方式进行调度(一个接一个,重复进行)。
  • 在某些系统中,优先级高的进程使用的时间片也较长,调度程序总是选择时间片未用尽而且优先级最高的进程运行,用户和系统都可以通过设置进程的优先级来影响系统调度。
  • Linux采用了两种不同的优先级范围,第一种是用nice值,它的范围是从―20到+19。第二种范围是实时优先级。
    可以用- ps-eo state,uid,pid,ppid,rtprio,time,comm.查看到你系统中的进程列衰,以及它们对应的实时优先级(位于RTPRIO 列下〉,其中如果有进
    程对应列显示“·”,则说明它不是实时进程。

4.3.3 时间片

时间片e是一个数值,它表明进程在被抢占前所能持续运行的时间。调度策略必须规定一个默认的时间片,但这并不是件简单的事,时间片过长会导致系统对交互的响应表现欠佳,让人觉得系统无法并发执行应用程序:时间片太短会明显增大进程切换带来的处理器耗因为肯定会有相当一部分系统时间用在进程切换上,而这些进程能够用来运行的时间片却很短,此外,I/O消耗型和处理器消耗型的进程之间的矛盾在这里也再次显露出来:I/O消耗型不需要长的时间片,而处理器消耗型的进程则希望越长越好(比如这样可以让它们的高速缓存命中率更高)

4.4 Linux调度算法

4.4.1 调度器类

Linux调度器是以模块方式提供的,这样做的目的是允许不同类型的进程可以有针对性地选择调度算法。这种模块化结构被称为调度器类。
它允许多种不同的可动态添加的调度算法并存,调度属于自己范畴的进程。
每个调度器都有一个优先级,基础的调度器代码定义在sched_fair.c文件中,它会按照优先级顺序遍历调度类,拥有一个可执行进程的最高优先级的调度器类胜出,去选择下面要执行的那一个程序。完全公平调度(CS)是一个针对普通进程的调度类,在Linux中称为SCHED_NORMAL。

4.4.2 Unix 系统中的进程调度

问题中的绝大多数都可以通过对传统Unix调度器进行改造解决,虽然这种改造修改不小,但也并非是结构性调整,比如,将nice值呈几何增加而非算数增加的方式解决第二个问题:采用一个新的度量机制将从nice值到时间片的映射与定时器节拍分离开来以此解决第三个问题。但是这些解决方案都回避了实质问题——即分配绝对的时间片引发的固定的切换频率,给公平性造成了很大变数,CFS采用的方法是对时间片分配方式进行根本性的重新设计(就进程调度器而言)完全摒弃时间片而是分配给进程一个处理器使用比重,通过这种方式,CFS的确保了进程调度中能有恒定的公平性,而将切换频率置于不断变动中。

4.4.3 公平调度

CFS的出发点基于一个简单的理念:进程调度的效果应如同系统具备―个备一个理想中的完美多任务同时,我们可以调度给它们无限小的时间周期,所以在任何可测量周期内,我们给予一个进程中每个进程同样多的运行时间。举例来说,假如我们有两个运行进程,在标准unⅸ调度模型中,我们先运行其中一个5ms,然后再运行另一个,5ms。但它们任何一个运行时都将占有100%的处理器。而在理想情况下,完美的多任务处理器模型应该是这样的:我们能在5ms内同时运行两个进程,它们各自使用处理器一半的能力。

4.5 Linux调度的实现

4.5.1 时间记账

所有的调度器都必须对进程运行时间做记账。多数Unix系统,分配一个时间片给每一个进程。那么当每次系统时钟节拍发生时,时间片都会被减少一个节拍周期。

  • 调度器实体结构
  • 虚拟实时

4.5.2 进程选择

在前面内容中我们的讨论中谈到若存在一个完美的多任务处理器,所有可运行进程的vruntime值将一致,但事实上我们没有找到完美的多任务处理器,因此CFS试图利用一个简单的规则去均衡进程的虚拟运行时间:当CFS的需要选择下一个运行进程时,它会挑一个具有最小vruntime的进程,这其实就是CSF调度算法的核心:选择具有最小、vruntime的任务。那么剩下的内容我们就来讨论到底是如何实现选择具有最小、vruntime值的进程。Linux中,红黑树称为rbtree,它是一个自平衡二叉搜索树,红黑树是一种以树节点形式存储的数据,这些数据都会对应一个键值,我们可以通过这些键值来快速检索节点上的数据(重要的是,通过键值检索到对应节点的速度与整个树的节点见模成指数比关系)

  • 挑选下一个任务
  • 向树中加入进程

4.5.3 调度器入口

进程调度的主要入口点是schedule(),它定义在文件kernel/sched.c中。

4.5.4 睡眠和唤醒

休眠(被阻塞)的进程处于一个特殊的不可执行状态,这点非常重要,如果没有这种特殊状态的话,调度程序就可能选出一个本不愿意被执行的进程,更糟糕的是,休眠就必须以轮询的方式实现了,进程休眠有多种原因,但肯定都是为了等待一些事件,事件可能是一段时间从文件读更多数据,或者是某个硬件事件,一个进程还有可能在尝试获取一个已被占用的内核信号量时被迫进入休眠,休眠的一个常见原因就是文件I/O——如进程对个文件执行了read()操作,而这需要从磁盘里读取,还有,进程在获取键盘输入的时候也需要等待,无论哪种情况,内核的操作都相同:进程把自己标记成休跳态,从可执照课树黑树中移出,放入等待队列,然后调用schedule()选择和执行―个其他进程,唤醒的过程刚好相反:进程被设置为可执行状态,然后再从等待队列中移到可晰红黑树中。

4.6 抢占和上下文切换

4.6.1 用户抢占

在内核返回用户空间的时候,它知道自己是安全的,因为既然它可以继续去执行当前进程,那么它当然可以再去选择一个新的进程去执行。所以,内核无论是在中断处理程序还是在系统调用后返回,都会检查need_resched标志,如果它被设置了,那么,内核会选择一个其他(更合适的进程投入运行。从中断处理程序或系统调用返回的返回路径都是跟体系结构相关的,在entry.S(此文件不仅包含内核入口部分的程序,内核退出部分的相关代码也在其中)文件中通过汇编语言来实现。简而言之,用户抢占在以下情况时产生:

  • 从系统调返回用户空间时。
  • 从中断处理程序返回用户空间时。

4.6.2 内核抢占

与其他大部分的Unⅸ变体和其他大部分的操作系统不同,Linux完整地支持内核抢占,在不支持内核抢占的内核中,内核代码可以一直执行,到它完成为止,也就是说,调度程序没有办法在一个内核级的任务正在执行的时候重新调度——内核中的各任务是以协作方式调度的不具备抢占性。内核代码一直要执行到完成(返回用户空间)或明显的阻塞为止,在2.6版的内核中,内核引入了抢占能力;现在,只要重新调度是安全的,内核就可以在任何时间抢占正在执行的任务。

4.7 实时调度策略

Linux的实时调度算法提供了―种软实时工作方式,软实时的含义是,内核调度进程,尽力使进程在它的限定时间到来前运行,但内核不保证总能满足这些进程的要求。相反,硬实时系统保证在一定条件下,可以满足任何调度的要求。Linux对于实时任务的调度不做任何保证。虽然不能保证硬实时工作方式,但Linux的实时调度算法的性能还是很不错的。2.6版的内核可以满足严格的时间要求。

4.8 与调度相关的系统调用

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

sched_setscheduler()和 sched_getscheduler()分别用于设置和获取进程的调度策略和实时优先级。

与其他的系统调用相似,它们的实现也是由许多参数检查、初始化和清理构成的。

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

sched_setscheduler()和 sched_getscheduler()分别用于设置和获取进程的实时优先级。

这两个系统调用获取封装在sched_param特殊结构体的rt_priority中。实时调度策略的的最大优先级:是MAX_ USERRT_PRIO减1。最小优先级等于1。

对于―个普通的进程,nice函数可以将给定进程的静态优先级增加一个给定的量。

只有超级用户才能在调用它时使用负值,从而提高进程的优先级。

nice函数会调用内核的set_user_nice函数,这个函数会设置进程的的task_struct的static_prio值。

4.8.2 与处理器绑定有关的系统调用

Linux调度程序提供强制的处理器绑定机制。

也就是说,虽然它尽力通过一种软的(或者说自然的)亲和性试图使进程尽量在同一个处理器上运行,但它也允许用户强制指定“这个进程无论如何都必须在这些处理器上运行”。

这种强制的亲和性保存在进程的一个位掩码标志中。

该掩码标志的每一位对应一个系统可用的处理器,默认情况下,所有的位都被设置。

《Linux内核设计与实现》第四章读书笔记的更多相关文章

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

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

  2. 《Linux内核设计与实现》第四周读书笔记——第五章

    <Linux内核设计与实现>第四周读书笔记--第五章 20135301张忻 估算学习时间:共1.5小时 读书:1.0 代码:0 作业:0 博客:0.5 实际学习时间:共2.0小时 读书:1 ...

  3. 《Linux内核设计与实现》Chapter 3 读书笔记

    <Linux内核设计与实现>Chapter 3 读书笔记 进程管理是所有操作系统的心脏所在. 一.进程 1.进程就是处于执行期的程序以及它所包含的资源的总称. 2.线程是在进程中活动的对象 ...

  4. 《Linux内核设计与实现》Chapter 1 读书笔记

    <Linux内核设计与实现>Chapter 1 读书笔记 一.Unix的特点 Unix从Multics中产生,是一个强大.健壮和稳定的操作系统. 特点 1.很简洁 2.在Unix系统中,所 ...

  5. 《Linux内核设计与实现》Chapter 2 读书笔记

    <Linux内核设计与实现>Chapter 2 读书笔记 一.获取内核源码 1.使用Git 我们曾经在以前的学习中使用过Git方法 $ git clone git://git.kernel ...

  6. 《Linux内核设计与实现》Chapter 5 读书笔记

    <Linux内核设计与实现>Chapter 5 读书笔记 在现代操作系统中,内核提供了用户进程与内核进行交互的一组接口,这些接口的作用是: 使应用程序受限地访问硬件设备 提供创建新进程与已 ...

  7. 《Linux内核设计与实现》Chapter 18 读书笔记

    <Linux内核设计与实现>Chapter 18 读书笔记 一.准备开始 一个bug 一个藏匿bug的内核版本 知道这个bug最早出现在哪个内核版本中. 相关内核代码的知识和运气 想要成功 ...

  8. LINUX内核设计与实现第三周读书笔记

    LINUX内核设计与实现第三周读书笔记 第一章 LINUX内核简介 1.1 Unix的历史 1969年的夏天,贝尔实验室的程序员们在一台PDR-7型机上实现了Unix这个全新的操作系统. 1973年, ...

  9. Linux内核设计与实现第十周读书笔记

    第十七章 设备与模块 关于设备驱动与设备管理,我们讨论四种内核成分. 设备类型 模块 内核对象 sysfs 17.1设备类型 在Linux以及所有Unix系统中,设备被分为以下三种类型: 块设备,块设 ...

  10. Linux内核设计与实现第八周读书笔记

    第四章 进程调度 进程在操作系统看来是程序的运行态表现形式. 4.1多任务 多任务操作系统就是能同时并发地交互执行多个进程的操作系统. 多任务操作系统会使多个进程处于堵塞或者睡眠状态.这些任务尽管位于 ...

随机推荐

  1. 【PAT】B1075 链表元素分类(25 分)

    这道题算有点难,心目中理想的难度. 不能前怕狼后怕虎,一会担心超时,一会又担心内存过大,直接撸 将三部分分别保存到vector 有意思的在于输出 分别输出第一个的add和num 中间输出nextadd ...

  2. January 30th, 2018 Week 05th Tuesday

    The things you own end up owning you. 你占有的东西终将会占有你. When we are longing for something, we would be w ...

  3. 1024. Video Stitching

    //使用java dfs public int videoStitching(int[][] clips, int T) { //bfs Queue<Integer> queue = ne ...

  4. 控件_DatePicker

    import android.app.Activity; import android.os.Bundle; import android.view.View; import android.view ...

  5. cenos下配置Apache+PHP最新版7.1.6+MySQL最新版(5.7.18)

    一.安装Apache,略. 二.安装PHP 1.下载php7 wget -O php7.tar.gz http://cn2.php.net/distributions/php-7.1.6.tar.gz ...

  6. LeetCode - 排列相关题目

    1.获取全排列 https://leetcode.com/problems/permutations/submissions/ 按字典序输出: 这里用的是vector<int>,不是引用. ...

  7. 解决普通用户登录ulimit 报错问题

    [root@master1 ~]# su - fengjian-bash: ulimit: open files: cannot modify limit: Operation not permitt ...

  8. mocha测试框架-truffle

    https://mochajs.org/
学习网址:
https://www.jianshu.com/p/9c78548caffa
https://www.jb51.net/article/10646 ...

  9. python3 day03 大纲

    一. 上次课内容回顾 1. 格式化输出 %d 占位数字 %s 占位字符串 %f 占位小数 "jfklasjkfj%s %d %f" % (值1, 值2, 值3) "{}爱 ...

  10. glVertexAttribPointer 用法简介

    在内存中采用交叉模式存储,向gpu传入顶点数据的方法  GPU: #version 100 attribute highp vec2 aPosition; attribute highp vec2 a ...