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

调度程序负责决定将哪个进程投入运行,何时运行和运行多长时间。

进程调度程序可看做在可运行态进程之间分配有限的处理器时间资源的内核子系统。

1 多任务

多任务操作系统就是能同时并发交互执行多个进程的操作系统。
实际上,Linux系统中有很多进程在内存中,但是只有一个处于可运行状态

多任务系统分类两类:

  • 非抢占式多任务
    除非进程自己主动停止运行,否则它会一直执行。进程主动挂起称为“让步”。

  • 抢占式多任务
    在此模式下,由调度程序来决定什么时候停止一个进程的运行。这种强制的挂起动作叫“抢占”。进程被抢占前有一个预先设置好的时间——进程的时间片,它是分配给每个可运行进程的处理器时间段。


2 Linux的进程调度

Linux2.5开发系列采用一种叫 O(1) 调度程序的新调度程序。
此调度程序虽然对于大服务器的工作负载很理想,但在有很多交互程序要运行的桌面系统上运行不佳。
因此
Linux2.6内核系统引入了“反转楼梯最后期限调度算法”(RSDL),此算法吸取了队列理论。
最终
Linux2.6.23内核版本中替代O(1) 调度算法,称为**“完全公平调度算法”,简称CFS**。


3 策略

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

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

  • I/O消耗型
    顾名思义,进程大部分时间用来提交或等待I/O请求。
  • 处理器消耗型
    大部分时间用在执行代码上。

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

进程优先级

最基本调度算法基于优先级:高先低后同轮转

Linux 采用两种优先级范围:

  • nice值
    范围是-20到+19,默认值为0。越大nice值表示更低的优先级。
  • 实时优先级
    其值可配置,默认情况变化范围从0到99。与nice值意义相反,实时优先级越高优先级越高。
    任何实时进程的优先级高于普通进程。

Linux查看进程优先级:
在这里插入图片描述

时间片

是一个数值,表明进程在被抢占前所能持续运行的时间。

时间片过长过短都会存在问题。

  • 过长:交互响应表现差
  • 过短:增大进程切换带来的处理器耗时

Linux是抢占式的,是否将一个进程投入使用,完全由进程优先级和是否有时间片决定的。
Linux使用新的CFS调度器,其抢占时机取决于新的可运行程序消耗多少处理器使用比。如果新进程消耗的使用比比当前进程小,立刻投入运行。


4 Linux调度算法

调度器类

Linux调度器以模块方式(调度器类)提供,不同进程可选择不同调度算法。
调度器也有优先级,会按照优先级顺序遍历调度类,选择最高优先级调度器类去执行程序。

Unix系统中的进程调度

在Unix系统,优先级通过nice值形式输出给用户空间,但会存在问题如下:

  1. nice值映射到时间片,会导致进程切换无法最优化
  2. 相对nice值,100ms与95ms相差5,10ms与5ms相差也是5,但是10ms比5ms多了两倍的处理器时间
  3. nice值与时间片的映射,我们需分配一个绝对时间片,并且它必须能在内核的测试范围内

CFS根本性确保了公平性:完全摈弃时间片而是分配给进程一个处理器使用比重

CFS

CFS为什么公平?因为其确保给每个进程公平的处理器使用比。每个进程将能获得1/n的处理器时间

允许每个进程运行一段时间、循环轮转、选择运行最少的进程作为下一个运行进程,而不再采用分配给每个进程时间片的做法。在所有可运行进程总数基础上计算出一个进程应该运行多久,而不是靠nice值计算时间片,而是当作进程获得的处理器运行比的权重,nice值越高权重越低。

而每个进程都按其权重在全部可运行进程中所占比例的“时间片”来运行。

但是如果可运行任务数量趋于无限,则每个获得的处理器使用比和时间片都趋于0,所以需要给每个进程设立时间片底线,即最小粒度(默认1ms)。

任何进程所获得的处理器时间是由它自己和其他所有可运行进程nice值的相对差值决定的。


5 Linux 调度的实现

时间记账

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

进程选择

当CFS需要选择下一个运行进程时,会挑一个具有最小vruntime的进程。
数据结构采用红黑树来组织可运行进程队列,而vruntime最小的进程便是红黑树中最左侧的叶子结点。

此过程涉及红黑树中结点查找、结点插入、结点删除。

调度器入口

进程调度的主要入口是函数 schedule():选择哪个进程运行,何时将其投入运行。
具体来说它会找到一个最高优先级的调度类(跟具体的调度类相关联)

睡眠和唤醒

  • 睡眠(被阻塞)
    如果进程要睡眠,则进程把自己标记成休眠状态,从可执行红黑树中移出,放入等待队列, 然后调用schedule() 选择和执行一个其他进程。
  • 唤醒
    进程被设置为可执行状态,从等待队列移到可执行红黑树中。
    通过函数 wake_up() 进行,其唤醒指定的等待队列上的所有进程。它调用函数 try_to_wake_up(),负责将进程设置为TASK_RUNNING状态。
    在这里插入图片描述

6 抢占和上下文切换

上下文切换:从一个可执行进程切换到另一个可执行进程,通过 context_switch() 函数负责处理。当一个新进程被选出来准备运行时,schedule() 就会调用该函数。过程如下:

  • switch_mm():负责将虚拟内存从上一进程映射到新进程中。
  • switch_to():负责从上一个进程的处理器状态切换到新进程的处理器状态。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值