《Linux内核设计与实现》课本第四章学习总结

进程调度

4.1 多任务

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

多任务系统分为两种:

  • 抢占式多任务:Linux提供了抢占式的多任务模式,由调度程序来决定什么时候停止一个进程的运行。

    现代操作系统提供:动态时间片计算的方式;可配置的计算策略

  • 非抢占式多任务:除非进程自己主动停止运行,否则会一直执行。

    调度程序无法躲每个进程该执行多长时间作出统一规定,所以进程独占的处理器时间可能会超过用户的预料

4.2 Linux的进程调度

O(1)调度器:对大服务器的工作负载很理想;但是缺少交互进程。

RSDL,反转楼梯最后期限调度算法(又被称为CFS,完美公平调度算法。)

4.3 策略

策略决定调度程序在合适让什么程序运行。

(一)两种典型的进程

1.  I/O消耗性进程

进程的大部分时间用来提交I/O请求或者等待I/O请求,经常处于可运行状态但是运行时间很短,等待更多的请求时最后总会阻塞。

2.处理器耗费型进程

把时间大多用在执行代码上,除非被抢占,否则通常都会不停运行。

调度策略通常要在两个矛盾的目标中间寻找平衡:

  • 进程调度迅速(响应时间短)
  • 最大系统利用率(高吞吐量)

(二)进程优先级

调度算法中最基本的一类就是基于优先级的调度这是一种根据进程的价值和其对处理器时间的需求来对进程分级的想法。

调度程序总是选择时间片未用尽而且优先级最高的进程运行。

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

  • nice

    范围[-20,19],默认值为0;
    nice值越大,优先级越低;
    Linux系统中nice值代表时间片的比例;
    ps-el命令查看系统中进程列表,NI列为nice值。

  • 实时优先级

    值可以配置,默认变化范围是[0,99];
    值越高优先级越高;
    任何实时进程的优先级都高于普通的进程。


(三)时间片

时间片表示进程在被抢占前所能持续运行的时间。

  • I/O消耗型进程不需要很长的时间片
  • 处理器消耗型进程希望时间片越长越好

Linux的CFS调度器没有直接分配时间片到进程,而是将处理器的使用比划分给进程。进程所获得的处理器时间和系统负载密切相关。这个比例受nice值影响,nice值作为权重来调整进程所使用的处理器时间使用比:

  • 高nice(低优先权)将被赋予低权重,从而损失小部分处理器使用比;
  • 低nice值(高优先权)将被赋予高权重,从而抢得更多处理器使用比。

Linux进程是抢占式的,是否抢占完全由进程的优先级和是否有时间片来决定。

CFS抢占器:抢占时机取决于新的可执行程序消耗了多少处理器使用比,如果消耗的使用比当前进程小:新程序立刻投入运行,抢占当前进程,否则推迟。

4.4 Linux调度算法

(一)调度器类

调度器类:允许多种不同的可动态添加的调度算法并存,调度属于自己范畴的进程。每个调度器有一个优先级,会按照优先级顺序遍历调度类,选择优先级最高的调度器类。

完全公平调度CFS是一个针对普通进程的调度类。

(二)Unix系统中的进程调度

Unix使用的调度算法是分配绝对的时间片,这样就会引发固定的切换频率,不利于公平性。
而Linux采用的CFS完全摒弃了时间片,分配给进程一个处理器使用比重,保证恒定的公平性和变动的切换频率。

(三)公平调度CFS

允许每个进程运行一段时间、循环轮转、选择运行最少的进程作为下一个运行进程,在所有可运行进程总数基础上计算出一个进程应该运行多久。nice值作为进程获得的处理器运行比的权重。

4.5 Linux调度的实现

CFS调度算法实现的四个组成部分:

  • 时间记账
  • 进程选择
  • 调度器入口
  • 睡眠和唤醒

(一)时间记账

所有的调度器都必须对进程运行时间做记账。

1.调度器实体结构

CFS不再有时间片的概念,但也必须维护每个进程运行的时间记账。使用调度器实体结构来追踪进程运行记账:进程描述符中的se变量。

2.虚拟实时

CFS使用了vruntime变量来存放进程的虚拟运行时间,用来表示进程到底运行了多少时间,以及它还应该运行多久。

这个虚拟运行时间是加权的,与定时器节拍无关。定义在kernel/sched_fair.c文件中的update_curr()函数实现了该记账功能。

它计算了当前进程的执行时间并存放入变量delta_exec中,然后又将运行时间传递给__update_curr();
__update_curr()根据当前可运行进程总数对进行时间进行加权计算,最终将权重值与当前运行进程的vruntime值相加。

(二)进程选择

CFS调度算法的核心:选择具有最小vruntime的任务

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

Linux中,红黑树被称为rbtree,是一个自平衡二叉搜索树,是一种以树节点形式存储的数据,这些数据会对应一个键值,可以通过这些键值来快速检索节点上的数据,而且检索速度与整个树的节点规模成指数比关系。

1.挑选下一个任务

节点键值是可运行进程的虚拟运行时间,CFS调度器随机选择待运行的下一个进程,是所有进程中vruntime最小的那个,它对应的是树中最左侧的叶子节点。函数是__pick_next_entity()

这个函数本身不会遍历树找到最左叶子节点,该值缓存在rb_leftmost字段中,
函数返回值就是CFS选择的下一个运行进程。
如果返回NULL,表示树空,没有可运行进程,这时选择idle任务运行。
2.向树中加入进程

发生在进程被唤醒或者通过fork调用第一次创建进程时。

函数enqueue_entity():更新运行时间和其他一些统计数据,然后调用__enqueue_entity()。
函数__enqueue_entity():进行繁重的插入工作,把数据项真正插入到红黑树中:
3.从树中删除进程

删除动作发生在进程堵塞或终止时。相关函数是dequeue_entity()和__dequeue_entity():

(三)调度器入口

进程调度的主要入口点函数是schedule()。

schedule()函数会调用pick_next_task();
pick_next_task()会以优先级为序依次检查每一个调度类,并且选择最高优先级的进程。
pick_next_task()会返回指向下一个可运行进程的指针,没有时返回NULL
pick_next_task()函数实现会调用pick_next_entity()
pick_next_entity()会调用__pick_next_entity()。

(四)睡眠和唤醒

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

唤醒时:进程被设置为可执行状态,然后再从等待队列中移到可执行红黑树中。

1.等待队列

等待队列是由等待某些事件发生的进程组成的简单链表,休眠通过等待队列进行处理。内核用wake _ queue head t来表示等待队列。等待队列可以通过DECLARE _ WAITQUEUE()静态创建,也可以由init _ waitqueue _ head()动态创建

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

  1. 调用宏DEFINE_WAIT()创建一个等待队列的选项。
  2. 调用add _ wait _ queue()把自己加入到队列中。
  3. 调用prepare _ to _ wait()方法将进程的状态变更为TASK _ INTERRUPTIBLE或TASK _ UNINTERRUPTIBLE。
  4. 如果状态被设置成TASK_INTERRUPTIBLE,则信号唤醒进程。(伪唤醒:唤醒不是因为事件的发生。)
  5. 当进程被唤醒的时候,会再次检查条件是否为真,真则退出循环,否则再次调用schedule()并且一直重复这步动作。
  6. 当条件满足后,进程将自己设置为TASK _ RUNNING并调用finish _ wait()方法把自己移出等待序列。函数inotify _ read():负责从通知文件描述符中读取信息。
2.唤醒

唤醒操作通过函数wake_up()进行,会唤醒指定的等待队列上的所有进程。

wake_up()函数调用try_to_wake_up()
try_to_wake_up()函数负责将进程设置成TASK_RUNNING状态
调用enqueue_task()将此进程放入红黑树中
如果被唤醒的进程优先级比正在执行的进程优先级高,设置need_resched标志
通常哪段代码促成等待条件达成,它就负责随后调用wake_up()函数。

4.6 抢占和上下文切换

上下文切换由context _ switch()函数负责。
每当一个新进程被选出准备投入运行时,schedule()会调用context _ switch()。
它完成了两项基本工作:

  • 调用switch_mm(),该函数负责把虚拟内存从上一个进程映射到新进程中。
  • 调用switch _ to(),该函数负责从上一个进程的处理器状态切换到新进程的处理器状态。
    这包括保存、恢复栈信息和寄存器信息,还有其他任何与体系结构相关的状态信息,都必须以每个进程为对象进行管理和保存。

1.用户抢占

内核即将返回用户空间的时候,如果need_resched标志被设置,会导致schedule()被调用,此时会发生用户抢占。

用户抢占发生的情况:

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

2.内核抢占

Linux完整地支持内核抢占。只要重新调度是安全的,内核就可以在任何时间抢占正在执行的任务。

锁是非抢占区域的标志。只要没有持有锁,内核就可以进行抢占。

支持内核抢占而完成的动作:

  1. 为每个进程的thread _ info中加入preempt _ count计数器,初值为0,使用锁+1,释放锁-1,数值为0时,可以执行抢占。
  2. 从中断返回内核空间时,先检查need_resched标志,如果被设置表示需要被调度,然后检查preempt_count计数器,如果为0,表示可以被抢占,这时调用调度程序。否则,内核直接从中断返回当前执行进程。
  3. 当前进程持有的锁全部被释放,这时preempt_count归0,释放锁的代码会检查need_resched是否被设置,如果是就调用调度程序。
  4. 如果内核中的进程被阻塞了,或者显式地调用了schedule(),内核抢占也会显式地发生。

内核抢占会发生在:

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

4.7 实时调度策略

Linux提供了两种实时调度策略:SCHED _ FIFO和SCHED _ RR。普通的非实时的调度策略是SCHED _ NORMAL。

SCHED_FIFO

简单的先入先出算法,不使用时间片

可运行的SCHED _ FIFO比任何SCHED _ NORMAL进程更先得到调度。只有更高优先级的FIFO或者RR才能抢占它;同等优先级的FIFO轮流执行,只有它愿意让出时才会退出。

SCHED_RR

带有时间片的FIFO,是一种实施轮流调度算法。

当RR耗尽它的时间片时,在同一优先级的其他实时进程被轮流调度。时间片只用来重新调度同一优先级进程。

优先级范围

  • 实时:0~[MAX _ RT _ PRIO-1]
    默认MAX _ RT _ PRIO=100,所以默认实时优先级范围为[0,99]
  • SCHED _ NORMAL:[MAX _ RT _ PRIO]~[MAX _ RT _ PRIO+40]。默认情况下,nice值从-20到+19对应的是从100到139的实时优先级范围。

4.8 与调度相关的系统应用

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

  • getpriority()/setpriority() 设置优先级
  • sched _ getscheduler()/sched _ setscheduler() 设置和获取进程的调度策略和实时优先级
  • sched _ getparam()/sched _ setparam() 设置和获取进程的实时优先级
  • sched _ get _ priority _ min()/sched _ get _ priority _ max() 返回给定调度策略的最大和最小优先级

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

  • Linux调度程序提供强制的处理器绑定机制
  • task _ struct中的cpus _ allowed位掩码中
  • sched_setaffinity() 设置不同的一个或者几个位组合的位掩码
  • sched _ getaffinity() 返回当前的cpus_ allowed位掩码

3.放弃处理器时间

sched_yield() 让进程显式地将处理器时间让给其他等待执行进程。普通进程移到过期队列中,实时进程移到优先级队列最后。

内核先调用yield,确定给定进程确实处于可执行状态,然后调用sched _ yield()。

用户空间可以直接调用sched _ yield()。

时间: 2024-12-28 09:56:27

《Linux内核设计与实现》课本第四章学习总结的相关文章

《Linux内核设计与实现》第四章学习笔记

第四章 进程调度 [学习时间:1小时45分 撰写博客时间:2小时10分钟] [学习内容:Linux的进程调度实现.抢占和上下文切换.与调度相关的系统调用] 调度程序负责决定将哪个进程投入运行,何时运行以及运行多长时间.进程调度程序可看做在可运行态进程之间分配有限的处理器时间资源的内核子系统. 最大限度利用处理器时间的原则:只要有可以执行的进程,那么总会有程序正在执行. 一.多任务 1.概念:多任务操作系统就是能同时并发地交互执行多个进程的操作系统,在单处理器机器上这会产生多个进程在同时运行的幻觉

《Linux内核设计与实现》——第四章 进程调度

<Linux内核设计与实现>——第四章 进程调度 4.1多任务 多任务操作系统:同时并发地交互执行多个进程的操作系统 多任务操作系统会使多个进程处于堵塞或者睡眠状态.这些任务尽管位于内存,但是并不处于可运行状态.这些进程利用内核堵塞自己,直到某一事件发生. 多任务系统可以划分为两类:非抢占式和抢占式. 抢占:强制挂起. 时间片:分配给每个可运行进程的处理器时间段. 4.3策略 策略决定调度程序在何时让什么程序运行. 4.3.1   I/O消耗型和处理器消耗型的进程 I/O消耗型:进程的大部分时

《Linux内核设计与实现》第四章读书笔记

第四章 进程调度 第4章 进程调度 调度程序负责决定将哪个进程投入运行,何时运行以及运行多长时间,进程调度程序可看做在可运行态进程之间分配有限的处理器时间资源的内核子系统.只有通过调度程序的合理调度,系统资源才能最大限度地发挥作用,多进程才会有并发行的效果. 调度程序没有太复杂的原理,最大限度地利用处理器时间的原则是只要有可以执行的进程,那么就总会有进程正在执行,但是只要系统中可运行的进程的数目比处理器的个数多,就注定某一给定时刻会有一些进程不能执行,这些进程在等待运行,在一组处于可运行状态的进

《Linux内核设计与实现》第三章学习笔记

第三章  进程管理 姓名:王玮怡  学号:20135116 一.进程 1.进程的含义 进程是处于执行期的程序以及相关资源的总称,程序本身并不是进程,实际上就是正在执行的代码的实时结果.Linux内核通常把进程也叫“任务”. 2.线程的含义 执行线程简称线程,是在进程中互动的对象.内核调度的对象是线程而不是进程.Linux系统不区分线程和进程,线程只是一种特殊的进程. 3.进程的执行过程 (1)clone()调用fork(),通过复制一个现有进程来创建一个全新的进程,进程开始存活.其中调用fork

《linux内核设计与实现》第四章

调度程序负责决定哪个进程投入运行,何时运行以及运行多长时间.只有通过调度程序合理调度,系统资源才能最大限度发挥作用,多进程才会有并发执行的效果. 最大限度地利用处理器时间的原则是,只要有可以执行的进程,那么就总会有进程正在执行. 1.多任务 多任务系统分两类:非抢占式多任务(cooperative multitasking)和抢占式多任务(preemptive multitasking).由调度器来决定什么时候停止一个进程运行,这个强制挂起动作叫做抢占(preemption). 进程在被抢占之前

《Linux内核设计与实现》第五章学习笔记

第五章 系统调用 操作系统中,内核提供了用户进程与内核进行交互的一组接口.这些接口让应用程序受限的访问硬件设备,提供了新进程与已有进程进行通信的机制,也提供了申请操作系统其他资源的能力.提供接口主要是为了保证系统稳定可靠,避免应用程序恣意妄行. 课堂知识点回顾 1. 系统调用:需要int 0x80模拟中断让硬件触发.同步.主动地进入系统空间. 2. 硬中断:异步.被动地进入系统空间.CPU运行时发生错误则中断,中断后没有进程调度. 3. 软中断:中断后还执行其他进程调度. 4. 系统调用过程:

《Linux内核设计与实现》第17章学习笔记

第17章.设备与模块 17.1设备类型 1.块设备(blkdev): 寻址以块为单位,通常支持重定位操作.通过称为“块设备节点”的特殊文件来访问. 2.字符设备(cdev): 不可寻址,仅提供数据的流式访问.通过称为“字符设备节点”的特殊文件来访问. 3.网络设备(ethernet devices): 提供了对网络的访问.它不是通过设备节点来访问,而是通过套接字API这样的特殊接口来访问. 17.2 模块 17.2.1 Hello,World (1)hello_init()函数是模块的入口点.

《Linux内核设计与实现》第八周学习总结——第四章 进程调度

<Linux内核设计与实现>第八周学习总结——第四章 进程调度 第4章 进程调度35 调度程序负责决定将哪个进程投入运行,何时运行以及运行多长时间,进程调度程序可看做在可运行态进程之间分配有限的处理器时间资源的内核子系统.只有通过调度程序的合理调度,系统资源才能最大限度地发挥作用,多进程才会有并发行的效果. 调度程序没有太复杂的原理,最大限度地利用处理器时间的原则是只要有可以执行的进程,那么就总会有进程正在执行,但是只要系统中可运行的进程的数目比处理器的个数多,就注定某一给定时刻会有一些进程不

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

chapter 4 进程调度 4.1 多任务 多任务操作系统就是能同时并发的交互执行多个进程的操作系统. 多任务系统可以划分为两类: - 非抢占式多任务: - 进程会一直执行直到自己主动停止运行(这一步骤称为让步) - 抢占式多任务: - Linux/Unix使用的是抢占式的方式:强制的挂起进程的动作就叫做抢占.进程在被抢占之前能够运行的时间是预先设置好的(也就是进程的时间片) 4.2 linux的进程调度 O(1)调度器:对大服务器的工作负载很理想,但是缺少交互进程. 反转楼梯最后期限调度算法

《Linux内核设计与实现》第一二章笔记

第一章 linux内核简介 每个处理器在任何时间点上的活动必然概括为下列三者: 运行于用户空间,执行用户进程 运行于内核空间,处于进程上下文,代表某个特定的进程执行 运行于内核空间,处于中断上下文,与任何进程无关,处理某个特定的中断 Linux内核与传统的Unix系统之间的差异: linux内核可以抢占 linux内核并不区分线程和其他一般进程 linux提供具有设备类的面向对象的设备模型.热插拔事件,以及用户空间的设备文件系统 linx忽略了一些被认为设计得很拙劣的unix特性,及难以实现的过