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

20135307 张嘉琪 第八周读书笔记算法


 

第四章 进程调度

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

  • 调度程序没有太复杂的原理,最大限度地利用处理器时间的原则是只要有能够执行的进程,那么就总会有进程正在执行,可是只要系统中可运行的进程的数目比处理器的个数多,就注定某一给定时刻会有一些进程不能执行,这些进程在等待运行,在一组处于可运行状态的进程中,选择一个来执行,是调度程序所需完成的基本工做安全

4.1 多任务

  • 多任务操做系统就是能同时并发地交互执行多个进程的操做系统,在单处理器机器上这会产生多个进程在同时运行的幻觉服务器

  • 在多处理器机器上,这会使多个进程在不一样的处理机上真正同时、并行地运行。不管在单处理器或者多处理器机器上,多任务操做系统都能使多个进程处于堵塞或者睡眠状态,也就是说,实际上不被投入执行,直到工做确实就绪。网络

  • 这些任务尽管位于内存,但并不处于可运行状态。相反,这些进程利用内核阻塞本身,直到某一事件(键盘输入网络数据、过一段时间等)发生。所以,现代Linux系统也许有100个进程在内存,可是只有一个处于可运行状态。并发

  • 多任务系统能够划分为两类:非抢占式多任务和抢占式多任务。像全部unix的变体和许多其余现代操做系统同样,Linux提供了抢占式的多任务。模块化

4.2 Linux的进程调度

  • O(1)调度程序——它是由于其算法的行为而得名的。函数

  • 静态时间片算法和针对每一处理器的运行队列,它们帮助咱们摆脱了先前调度程序设计上的限制。性能

  • 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。第二种范围是实时优先级。

4.3.3 时间片

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

4.3.4 调度策略的活动

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系统,分配一个时间片给每个进程。那么当每次系统时钟节拍发生时,时间片都会被减小一个节拍周期。

    1. 调度器实体结构
    2. 虚拟实时

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(此文件不只包含内核入口部分的程序,内核退出部分的相关代码也在其中)文件中经过汇编语言来实现。简而言之,用户抢占在如下状况时产生:
    1. 从系统调返回用户空间时。
    2. 从中断处理程序返回用户空间时。

4.6.2 内核抢占

  • 与其余大部分的Unix变体和其余大部分的操做系统不一样,Linux完整地支持内核抢占,在不支持内核抢占的内核中,内核代码能够一直执行,到它完成为止,也就是说,调度程序没有办法在一个内核级的任务正在执行的时候从新调度——内核中的各任务是以协做方式调度的不具有抢占性。

4.7 实时调度策略

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

4.8 与调度相关的系统调用

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

- schedsetscheduler()和 schedgetscheduler()分别用于设置和获取进程的调度策略和实时优先级。与其余的系统调用类似,它们的实现也是由许多参数检查、初始化和清理构成的。其实最重要的工做在于读取或改写进程taskstruct的policy和rtpriority的值。

  • schedsetscheduler()和 schedgetscheduler()分别用于设置和获取进程的实时优先级。这两个系统调用获取封装在schedparam特殊结构体的rtpriority中。实时调度策略的的最大优先级:是MAX USERRTPRIO减1。最小优先级等于1。

  • 对于一个普通的进程,nice函数能够将给定进程的静态优先级增长一个给定的量。只有超级用户才能在调用它时使用负值,从而提升进程的优先级。nice函数会调用内核的setusernice函数,这个函数会设置进程的的taskstruct的staticprio值。

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

  • Linux调度程序提供强制的处理器绑定机制。也就是说,虽然它尽力经过一种软的(或者说天然的)亲和性试图使进程尽可能在同一个处理器上运行,但它也容许用户强制指定“这个进程不管如何都必须在这些处理器上运行”。这种强制的亲和性保存在进程的一个位掩码标志中。该掩码标志的每一位对应一个系统可用的处理器,默认状况下,全部的位都被设置。

4.8.3 放弃处理器时间

  • Linux经过schedyield()系统调用提供了一种让进程显式地将处理器时间让给其余等待执行进程的机制,它是经过将进程从活动队列中(由于进程正在执行,因此它确定位于此队列当中)移到过时队列中实现的,由此产生的效果不只抢占了该进程并将其放入优先级队列的最后面,还将其放入过时队列中—这样能确保在一段时间内它都不会再被执行了,因为实时进程不会过时,因此属于例外,它们只被移动到其优先级队列的最后面(不会放到过时队列中)在Linux以的早期版本中,进程只会被放置到优先级队列的末尾,放弃的时间每每不会太长,如今,应用程序甚至内核代码在调用schedyield()前,应该仔细考虑是否真的但愿放弃处理器时间。内核代码为了方便,能够直接调用sched_yield(),先要肯定给定进程确实处于可执行状态,而后再调用schedyield(),用户空间的应用程序直接使用schedyield()系统调用就能够 。

.9 小结56

  • 进程调度程序是内核重要的组成部分,由于运行着的进程首先在使用计算机(至少在咱们大多数人看来)然而,知足进程调度的各类须要毫不是垂手可得的:很难找到“一刀切”的算法既适合众多的可运行进程,又具备可伸缩性,还能在调度周期和吞吐量之间求得平衡,同时还知足各类负载的需求,不过,Linux内核的新CFS调度程序尽可能知足了各个方面的需求,并以较完善的可伸缩性和新颖的方法提供了最佳的解决方案,前面的章节覆盖了进程管理的相关内容,本章则考察了进程调度所遵循的基本原理、具体实现、调度算法以及目前Linux内核所使用的接口。
相关文章
相关标签/搜索