深入理解Linux内核 chap 7 进程调度

来源:互联网 发布:淘宝店铺卖气模名字 编辑:程序博客网 时间:2024/05/01 15:30

1. 调度策略

  1. 目标:
    • 进程响应时间尽可能快
    • 后台作业的吞吐量尽可能高
    • 尽可能避免进程饥饿
    • 低优先级和高优先级进程需要尽可能调和
  2. Linux 的调度基于分时技术: 多个进程以“时间多路复用”的方式运行, 分时依赖于定时中断, 因此对进程是透明的
  3. 调度策略根据优先级进行分类, 每个进程都与一个值相关联, 这个值表示把进程如何适当的分配给CPU
  4. 在Linux 中, 进程优先级是动态的
    • 在较长时间间隔内没有使用CPU 的进程, 通过动态增加他们的优先级来提升他们
    • 对已经在CPU 上运行了较长时间的进程, 通过减少他们的优先级来处罚他们
  5. 进程分类1
    • IO 受限型
    • CPU 受限型
  6. 进程分类2
    • 交互式 (高优先级)
    • 批处理
    • 实时

1.1 进程抢占

  1. 被抢占的进程并没有被挂起, 还是处于TASK_RUNNING状态, 只是不再使用CPU

1.2 一个时间片必须持续多长

  1. 对时间片大小的选择始终是一种折衷, Linux 采用单凭经验的方法, 即选择尽可能长, 同时能保持良好相应时间的一个时间片
  2. 太长: 进程看起来就不像是并发运行的了
  3. 太短: 进程切换引起的系统额外开销就会变得特别高

2. 调度算法

  1. 调度类型:
    • SCHED_FIFO
    • SCHED_RR 时间片轮转
    • SCHED_NORMAL 普通分时

2.1 普通进程调度

  1. 新进程总是继承其父进程的静态优先级
  2. 通过 将 nice 值传递给系统调用 nice(), setpriority() 用户可以改变自己拥有的进程的静态优先级

2.1.1 基本时间片

  1. 静态优先级本质上决定了进程的基本时间片
  2. 静态优先级越高(值越小), 基本时间片就越长
    这里写图片描述

2.1.2 动态优先级和平均睡眠时间

  1. 普通进程除了静态优先级, 还有动态优先级, 范围是 100 ~ 139
  2. 动态优先级公式:
    =max(100,min(bonus+5,139))
  3. 在这个公式中, bonus 的值小于 5 表示惩罚, 值大于 5 表示 奖赏。 bonus 的值依赖于进程的过去情况(与进程的平均睡眠时间相关)
  4. 平均睡眠时间也 被调度程序用来确定进程是不是交互式进程还是批处理进程, 高优先级进程比低优先级进程更容易成为交互式进程

2.1.3 活动和过期进程

  1. 即使具有较高静态优先级的普通进程获得了较大的CPU时间片, 也不应该让静态优先级较低的进程无法运行(避免饥饿)
  2. 活动进程: 这些进程还没有用完他们的时间片, 因此允许他们运行
  3. 过期进程: 这些可运行进程已经用完了他们的时间片, 并因此被禁止运行, 直到所有的活动进程都过期

2.2 实时进程的调度

  1. 当系统调用nice 和 setpriority 用于基于时间片轮转的实时进程的时候, 不改变实时进程的优先级而会改变其基本时间片的长度。
  2. 基于时间片轮转的实时进程的基本时间片与实时进程的优先级无关, 只依赖于进程的静态优先级

3. 调度程序所使用的数据结构

3.1 数据结构runqueue

  1. 运行队列的arrays字段是包含两个prio_array_t结构的数组, 每个数据结构都表示一个可运行进程的集合, 并包含140 个双向链表头(每个链表头对应一个可能的进程优先级), 一个优先级位图和一个集合中所包含的进程数量的计数器
  2. 两个结构会发生周期性变化: 活动进程突然变成过期进程, 过期进程变为活动进程, 调度程序简单的交换运行队列的active 和 expired字段的内容, 来完成这个操作。
    这里写图片描述

3.2 进程描述符

  1. 父进程的剩余节拍数被划分为两等份, 一份给父进程, 一份给子进程, 这样做是为了避免用户通过某种方法获取无线的CPU时间
  2. 子进程没有用完的时间片会奖励给父进程

4. 调度程序所使用的函数

4.1 scheduler_tick 函数

  1. 每次节拍到来的时候, 函数被调用来执行相关调度操作

4.2 try_to_wake_up 函数

  1. 通过将进程状态设置为TASK_RUNNING, 并把该进程插入本地CPU的运行队列来唤醒睡眠或者停止的进程

4.3 recalc_task_prio 函数

  1. 更新进程的平均睡眠时间和动态优先级

4.4 schedule 函数

  1. 实现调度任务, 从运行队列的链表中找到一个进程, 并随后将CPU 分配给这个进程
  2. 可以由几个内核控制路径调用, 可以采取直接调用或延迟调用的方式

5. 多处理器系统中运行队列的平衡

  1. 多处理器类型:
    • 标准的多处理器体系结构
    • 超线程 (这个一个立刻执行几个执行线程的微处理器, 包括几个内部寄存器的拷贝, 并快速在他们之间切换)
    • NUMA (以节点为单位分组, 可以很快速的访问本节点的内容, 但是访问远程节点就比较慢了)

5.1 调度域

  1. 调度域实际上是一个CPU 的集合, 他们的工作量应当由内核保持平衡
  2. 每个调度域被依次划分为一个或者多个分组, 每个组代表调度域的一个CPU子集。 工作量的平衡总是在调度域的组之间来完成, ie, 只有当某个调度域的某个组的总工作量远远低于同一调度域的另一个组的工作量的时候, 才把进程从一个CPU迁移到另一个CPU。
    这里写图片描述

5.2 rebalance_tick

  1. 为了保持系统中运行队列的平衡, 没经过一次时钟节拍, schedule_tick 调用rebalance_tick

5.3 load_balance

  1. 检查调度域是否处在严重的不平衡状态

5.4 move_tasks

  1. 将进程从源运行队列迁移到本地运行队列

6. 与调度相关的系统调用

  1. nice
  2. getpriority, setpriority
  3. sched_getaffinity, sched_setaffinity
  4. sched_getscheduler , sched_setscheduler
  5. sched_getparam , sched_setparam
  6. sched_yield
  7. sched_get_priority_main, sched_get_priority_max
  8. sched_rr_get_interval
0 0
原创粉丝点击