第四章 进程调度

进程在操作系统看来是程序的运行态表现形式。

4.1多任务

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

多任务操作系统会使多个进程处于堵塞或者睡眠状态。这些任务尽管位于内存,但并不处于可运行状态。相反,这些进程利用内核堵塞自己,直到某一事件发生。

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

  • 强制的挂起动作就叫做抢占。
  • 时间片实际上就是分配给每个可运行进程的处理器时间段。

4.2 linux的进程调度

O(1)调度程序

4.3策略

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

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

  • I/O消耗型:进程的大部分时间用来提交I/O请求或是等待I/O请求。因此,这样的进程经常处于可运行状态,但通常都是运行短短一会儿,因为它在等待更多的I/O请求时总会阻塞。
  • 处理器消耗型:把时间大多用在执行代码上。除非被抢占,否则他们通常都一直不停地运行。调度策略往往是尽量降低他们的调度频率,而延长其运行时间。

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

4.3.2  进程优先级

这是一种根据进程的价值和其对处理器时间的需求来对进程分级的想法。
调度程序总是选择时间片未用尽而且优先级最高的进程运行。

Linux采用了两种不同的优先级范围:

  • 1、nice值:范围-20~+19,默认值为0;越大的nice值意味着更低的优先级。高优先级的进程可以获得更多的处理器时间。
  • 2、实时优先级:其值是可配置的,默认情况下的变化范围是0~99.越高的实时优先级数值意味着进程优先级越高。任何实时进程的优先级都高于普通的进程。

任何实时进程的优先级都高于普通进程,也就是说实时优先级和nice优先级处于互不相交的两个范畴。

4.3.3时间片

时间片过长会导致系统对交互的响应表现欠佳,太短会明显增大进程切换带来的处理器耗时。

进程所获得的处理器时间其实是和系统负载密切相关。这个比例进一步还会受进程nice值的影响。

4.4linux调度算法

4.4.1调度器类

Linux调度器是以模块方式提供的,这样做的目的是允许不同类型的进程可以有针对性地选择调度算法。这种模块化结构被称为调度器类。

每个调度器都有一个优先级,基础的调度器代码定义在kernel/sched.c文件中,它会按照优先级顺序来遍历调度类,拥有一个可执行进程的最高优先级的调度器类胜出,去选择下面要执行的那一个程序。

完全公平调度(CFS):针对普通进程的调度类,在linux中称为SCHED_NORMAL,CFS算法定义在文件kernel/sched_fair.c中。

4.4.2 unix进程中的进程调度

  • 第一个问题,若要将nice值映射到时间片,就必然需要将nice单位值对应到处理器的绝对时间,但这样做就会导致进程切换无法最优化进行。
  • 第二个问题涉及相对nice值,把进程的nice值减小1所带来的效果极大地取决于其nice 的初始值。
  • 第三个问题,如果执行nice 值到时间片的映射,时间片必须是定时器节拍的整数倍,系统定时器限制了两个时间片的差异。
  • 第四个问题,基于优先级的调度器为了优化交互任务而唤醒相关进程,为了进程能够尽快的投入运行,而去对新要唤醒的进程提升优先级,即使他们的时间片已经用尽,使得给定进程打破公平原则,获得更多处理器时间,损害系统中其他进程的利益。

CFS采用的方法是对时间片分配方式进行根本性的重新设计(就进程调度器而言);完全摒弃时间片而是分配给进程一个处理器使用比重。
 

4.4.3公平调度

我们希望所有进程能只运行一个非常短的周期,但是CFS充分考虑了这将带来的额外消耗,实现中首先要确保系统性能不受损失。

CFS的做法是允许每个进程运行一段时间、循环轮转、选择运行最少的进程作为下一个运行进程,而不再采用分配给每个进程时间片的做法。nice值在CFS中被作为进程获得的处理器运行比的权重:越高的nice 值进程获得更低的处理器使用权重。

为了计算准确的时间片,CFS为完美多任务中的无限小调度周期的近似值设立了“目标延迟”,越小的调度周期将带来越好的交互性,同时也更接近完美的多任务。CFS引入每个进程获得的时间片底线—最小粒度。默认情况下这个值是1ms。即便是可运行进程数量趋于无穷,每个最少也能获得1ms的运行时间,确保切换消耗被限制在一定范围内。

总结一下,任何进程获得的处理器时间是由它自己和其他所有可运行进程nice值的相对差值决定的。nice值对时间片的作用不再是算术加权,而是几何加权,任何nice值对应的绝对时间不再是一个绝对值,而是处理器的使用比。

4.5 linux调度的实现

4.5.1时间记账

当每次系统时钟节拍发生时,时间片都会被减少一个节拍周期。当一个进程的时间片被减少到0的时候,它就会被另一个尚未减到0的时间片可运行进程抢占。

1.调度器实体结构:用来追踪进程运行记账

 

   
调度器实体结构作为一个名为se的成员变量,嵌入在进程描述符struct task_struct内。
 

2.虚拟实时

vruntime变量存放进程的虚拟运行时间,该运行时间的计算是经过了所有可运行进程总数的标准化。虚拟时间以ns为单位,所以和定时器街拍不再相关。CFS使用vruntime变量来记录一个程序到底运行了多长时间以及它还应该再运行多久。 

update_curr()计算了当前进程的执行时间,并且将其存放在变量delta_exec中。然后他把运行时间传递给__update_curr(),由后者再根据当前可运行进程总数对运行时间进行加权计算,最终将上述的权值与当前运行进程的vruntime相加。  

update_curr()由系统定时器周期性调用。

4.5.2进程选择

当CFS需要选择下一个运行进程时,它会挑一个具有最小vruntime值得进程。

CFS使用红黑树来组织可运行进程队列,并利用其迅速找到最小vruntime值得进程。

1.挑选下一个任务

CFS的进程选择算法可简单总结为“运行rbtree树中最左边叶子节点所代表的那个进程。”实现这一过程的函数是__pick_next_entity()

2.向树中加入进程

发生在进程变为可运行状态(被唤醒)或者通过fork()调用第一次创建进程时。enqueue_entity()函数实现了这一目的。                   

该函数更新运行时间和其他一些统计数据,然后调用__enqueue_entity()进行繁重的插入操作,把数据项真正插入到红黑色树中。

3.从树中删除进程

删除动作发生在进程堵塞或者终止时。    

4.5.3调度器入口

进程调度的主要入口点是函数schedule(),它正是内核其他部分用于调用进程调度器的入口:选择哪个进程可以运行,何时将其投入运行。该函数中唯一重要的事情是,它会调用pick_next_task(),该函数会以优先级为序,从高到低,依次检查每一个调度类,并且从最高优先级的调度类中,选择最高优先级的进程。该函数的核心是for()循环,它以优先级为序,从最高的优先级类开始,遍历了每一个调度类。

4.5.4睡眠和唤醒

休眠:进程把自己标记成休眠状态,从可执行红黑树中移出,放入等待队列,然后调用schedule()选择和执行一个其他进程。

唤醒:进程被置为可执行状态,然后在从等待队列中移到可执行红黑树中。
休眠有两种相关的进程状态:TASK_INTERRUPTIBLETASK_UNINTERRUPTIBLE。他们唯一的区别是TASK_UNINTERRUPTIBLE的进程会忽略信号,而TASK_INTERRUPTIBLE状态的进程如果接收到一个信号,会被提前唤醒并响应该信号。两种状态的信号位于同一等待队列上,等待某些事件,不能够运行。

1.等待队列

进程通过执行下面几个步骤将自己加入到一个等待队列中:

2.唤醒

唤醒函数通过函数wake_up()进行,它会唤醒指定的等待队列上的所有进程。它调用函数try_to_wake_up(),该函数负责将进程设置为TASK_RUNNING状态,调用enqueue_task()将此进程放入红黑树中,如果被唤醒的进程优先级比当前就正在执行的进程优先级高,还要设置need_resched标志。
 

4.6抢占和上下文切换

由定义在kernel/sched.c中的context_switch()函数负责处理。每当一个新的进程被选出来准备投入运行的时候,schedule()就会调用该函数。它完成了下面两项基本工作:

  • 调用声明在<asm/mmu_contesxt.h>中的switch_mm(),该函数负责把虚拟内存从上一个进程映射切换到新进程中。
  • 调用声明在<asm/system.h>中的switch_to(),该函数负责从上一个进程的处理器状态切换到新进程的处理器状态。

4.6.1用户抢占

用户抢占在以下情况时产生:

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

4.6.2内核抢占

只要没有持有锁,内核就可以进行抢占。

内核抢占会发生在:

  • 中断处理程序正在执行,且返回内核空间之前。
  • 内核代码再一次具有可抢占性的时候。
  • 如果内核空间中的任务显式地调用schedule()。
  • 如果内核中的任务阻塞。(也会调用schedule())。
     

    4.7实时调度策略

    Linux提供了两种实时调度策略:SCHED_FIFOSCHED_RR

  • SCHED_FIFO:先入先出算法。 
  • SCHED_RR:带有时间片的先入先出算法。
     
    4.8与调度相关的系统调用
      

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

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

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

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

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

  3. Linux内核设计与实现第五周读书笔记

    第十八章 调试 18.1准备开始 需要的只是: 一个确定的bug.大部分bug通常都不是行为可靠而且定义明确的. 一个藏匿bug的内核版本. 相关的内核代码的知识和运气. 18.2内核中的bug 内核 ...

  4. Linux内核设计与实现第六周读书笔记

    第三章 进程管理 3.1 进程 进程是处于执行期的代码.通常进程还要包含其他资源,像打开的文件.挂起的信号.内核的内部数据.处理器状态.一个或多个具有内存映射的内存地址空间及一个或多个执行线程,当然还 ...

  5. linux内核设计与实现第七周读书笔记

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

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

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

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

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

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

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

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

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

随机推荐

  1. AtCoder | ARC103 | 瞎讲报告

    目录 ARC 103 A.//// B.Robot Arms C.Tr/ee D.Distance Sums ARC 103 窝是传送门QwQ A.//// 题意 : 给你\(n\)(\(n\)为偶数 ...

  2. IOS上z-index和fixed定位无效

    IOS上z-index和fixed定位无效 在该元素上加: -webkit-transform:translateZ(1px); -moz-transform:translateZ(1px); -o- ...

  3. maven实战读书笔记(三)

    maven将一系列的步骤都封装为一系列的插件,运行命令后一系列的插件运行

  4. oracle和mysql在sql中生成uuid的方法

    1,oracle sys_guid() 2,mysql uuid()

  5. [BUAA OO]第一次博客作业

    第一次作业 第一次进行面向对象的编程,不论是针对数据设计类还是对方法进行合适的归于不同类中,都不是很熟悉.所写出来的程序还是面向过程+有函数的类(虽然现在很大程度上感觉起来也是这样).索性作业难度并不 ...

  6. java实验1实验报告(20135232王玥)

    实验一 Java开发环境的熟悉 一.实验内容 1. 使用JDK编译.运行简单的Java程序 2.使用Eclipse 编辑.编译.运行.调试Java程序 二.实验要求 1.没有Linux基础的同学建议先 ...

  7. 《Spring1之第八次站立会议》

    <第八次站立会议> 昨天:我查找了关于实现视频功能的相关代码. 今天:对用C#写的视频功能进行了相关的了解. 遇到的问题:由于对C#不是很了解,所以其中的有些代码还是看不懂.

  8. BufferedInputStream 缓冲输入字节流 -------上

    package com.BufferedInputStreamUse; import java.io.BufferedInputStream; import java.io.File; import ...

  9. 软工实践-Beta 冲刺 (6/7)

    队名:起床一起肝活队 组长博客:博客链接 作业博客:班级博客本次作业的链接 组员情况 组员1(队长):白晨曦 过去两天完成了哪些任务 描述: 1.界面的修改与完善 展示GitHub当日代码/文档签入记 ...

  10. java中的装箱与拆箱

    什么是自动装箱拆箱 基本数据类型的自动装箱(autoboxing).拆箱(unboxing)是自J2SE 5.0开始提供的功能. 一般我们要创建一个类的对象实例的时候,我们会这样: Class a = ...