彻底理解Linux 进程调度所有知识点

进程的分类在 CPU 的角度看进程行为的话 , 可以分为两类:

  • CPU 消耗型:此类进程就是一直占用 CPU 计算 , CPU 利用率很高
  • IO 消耗型:此类进程会涉及到 IO , 需要和用户交互 , 比如键盘输入 , 占用 CPU 不是很高 , 只需要 CPU 的一部分计算 , 大多数时间是在等待 IO
CPU 消耗型进程需要高的吞吐率 , IO 消耗型进程需要强的响应性 , 这两点都是调度器需要考虑的 。
为了更快响应 IO 消耗型进程 , 内核提供了一个抢占(preempt)机制 , 使优先级更高的进程 , 去抢占优先级低的进程运行 。内核用以下宏来选择内核是否打开抢占机制:
  • CONFIG_PREEMPT_NONE: 不打开抢占 , 主要是面向服务器 。此配置下 , CPU 在计算时 , 当输入键盘之后 , 因为没有抢占 , 可能需要一段时间等待键盘输入的进程才会被 CPU 调度 。
  • CONFIG_PREEMPT : 打开抢占 , 一般多用于手机设备 。此配置下 , 虽然会影响吞吐率 , 但可以及时响应用户的输入操作 。
调度相关的数据结构先来看几个相关的数据结构:
task_struct我们先把 task_struct 中和调度相关的结构拎出来:
struct task_struct { ...... const struct sched_class *sched_class; struct sched_entity  se; struct sched_rt_entity  rt; ...... struct sched_dl_entity  dl; ...... unsigned int   policy; ......}
  • struct sched_class:对调度器进行抽象 , 一共分为5类 。
  1. Stop调度器:优先级最高的调度类 , 可以抢占其他所有进程 , 不能被其他进程抢占;
  2. Deadline调度器:使用红黑树 , 把进程按照绝对截止期限进行排序 , 选择最小进程进行调度运行;
  3. RT调度器:为每个优先级维护一个队列;
  4. CFS调度器:采用完全公平调度算法 , 引入虚拟运行时间概念;
  5. IDLE-Task调度器:每个CPU都会有一个idle线程 , 当没有其他进程可以调度时 , 调度运行idle线程;
  • unsigned int policy:进程的调度策略有6种 , 用户可以调用调度器里的不同调度策略 。
  1. SCHED_DEADLINE:使task选择Deadline调度器来调度运行
  2. SCHED_RR:时间片轮转 , 进程用完时间片后加入优先级对应运行队列的尾部 , 把CPU让给同优先级的其他进程;
  3. SCHED_FIFO:先进先出调度没有时间片 , 没有更高优先级的情况下 , 只能等待主动让出CPU;
  4. SCHED_NORMAL:使task选择CFS调度器来调度运行;
  5. SCHED_BATCH:批量处理 , 使task选择CFS调度器来调度运行;
  6. SCHED_IDLE:使task以最低优先级选择CFS调度器来调度运行;

彻底理解Linux 进程调度所有知识点

文章插图
 
  • struct sched_entity se:采用CFS算法调度的普通非实时进程的调度实体 。
  • struct sched_rt_entity rt:采用Roound-Robin或者FIFO算法调度的实时调度实体 。
  • struct sched_dl_entity dl:采用EDF算法调度的实时调度实体 。
分配给 CPU 的 task , 作为调度实体加入到运行队列中 。
runqueue 运行队列runqueue 运行队列是本 CPU 上所有可运行进程的队列集合 。每个 CPU 都有一个运行队列 , 每个运行队列中有三个调度队列 , task 作为调度实体加入到各自的调度队列中 。
struct rq { ...... struct cfs_rq cfs; struct rt_rq rt; struct dl_rq dl; ......}三个调度队列:
  • struct cfs_rq cfs:CFS调度队列
  • struct rt_rq rt:RT调度队列
  • struct dl_rq dl:DL调度队列

彻底理解Linux 进程调度所有知识点

文章插图