From a0471e0c8c2873eee41d415913dc6fb35239127b Mon Sep 17 00:00:00 2001 From: gatieme Date: Thu, 16 Jun 2016 22:06:33 +0800 Subject: [PATCH] =?UTF-8?q?=E8=BF=9B=E7=A8=8B=E8=B0=83=E5=BA=A6...?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../01-process/05-schedule/01-/README.md | 179 +++++++++++++++++- 1 file changed, 176 insertions(+), 3 deletions(-) diff --git a/study/kernel/01-process/05-schedule/01-/README.md b/study/kernel/01-process/05-schedule/01-/README.md index c253b6a..228e7c0 100644 --- a/study/kernel/01-process/05-schedule/01-/README.md +++ b/study/kernel/01-process/05-schedule/01-/README.md @@ -43,7 +43,7 @@ Linux进程调度器的组成 * 低优先级和高优先级进程的需要尽可能调和等等 -调度策略(scheduling policy)的任务就是决定什么时候以怎么样的方式选择一个新进程占用CPU运行 +调度策略(scheduling policy)的任务就是决定什么时候以怎么样的方式选择一个新进程占用CPU运行. 传统操作系统的调度基于分时(time sharing)技术: 多个进程以"时间多路服用"方式运行, 因为CPU的时间被分成"片(slice)", 给每个可运行进程分配一片CPU时间片, 当然单处理器在任何给定的时刻只能运行一个进程. @@ -73,7 +73,7 @@ Linux进程调度器的组成 #进程的分类 ------- -##进程分类的方法 +##进程的分类 ------- 当涉及有关调度的问题时, 传统上把进程分类为"I/O受限(I/O-dound)"或"CPU受限(CPU-bound)". @@ -193,9 +193,182 @@ linux进程的调度算法其实经过了很多次的演变, 但是其演变主 * 在选中将要运行的进程之后, 必须执行底层的任务切换. 这需要与CPU的紧密交互. 每个进程刚好属于某一调度类, 各个调度类负责管理所属的进程. 通用调度器自身不涉及进程管理, 其工作都委托给调度器类. + #进程调度的数据结构 ------- 调度器使用一系列数据结构来排序和管理系统中的进程. 调度器的工作方式的这些结构的涉及密切相关, 几个组件在许多方面 -核心 +##[task_struct中调度相关的成员](http://lxr.free-electrons.com/source/include/linux/sched.h?v=4.5#L1394) +------- + +```c +/* 优先级 */ +int prio, static_prio, normal_prio; +unsigned int rt_priority; + +/* */ +const struct sched_class *sched_class; +struct sched_entity se; +struct sched_rt_entity rt; + +#ifdef CONFIG_CGROUP_SCHED + struct task_group *sched_task_group; +#endif +struct sched_dl_entity dl; + + +unsigned int policy; +int nr_cpus_allowed; +cpumask_t cpus_allowed; +``` + + +###优先级 +------- + +```c +int prio, static_prio, normal_prio; +unsigned int rt_priority; +``` + + +**动态优先级 静态优先级 实时优先级** + + +其中task_struct采用了三个成员表示进程的优先级:prio和normal_prio表示动态优先级, static_prio表示进程的静态优先级. + +>为什么表示动态优先级需要两个值prio和normal_prio +> +>调度器会考虑的优先级则保存在prio. 由于在某些情况下内核需要暂时提高进程的优先级, 因此需要用prio表示. 由于这些改变不是持久的, 因此静态优先级static_prio和普通优先级normal_prio不受影响. + +此外还用了一个字段rt_priority保存了实时进程的优先级 + +| 字段 | 描述 | +| ------------- |:-------------:| +| static_prio | 用于保存静态优先级, 是进程启动时分配的优先级, ,可以通过nice和sched_setscheduler系统调用来进行修改, 否则在进程运行期间会一直保持恒定 | +| prio | 保存进程的动态优先级 | +| normal_prio | 表示基于进程的静态优先级static_prio和调度策略计算出的优先级. 因此即使普通进程和实时进程具有相同的静态优先级, 其普通优先级也是不同的, 进程分叉(fork)时, 子进程会继承父进程的普通优先级 | +| rt_priority | 用于保存实时优先级 | + + +实时进程的优先级用实时优先级rt_priority来表示 + +linux2.6内核将任务优先级进行了一个划分, 实时优先级范围是0到MAX_RT_PRIO-1(即99),而普通进程的静态优先级范围是从MAX_RT_PRIO到MAX_PRIO-1(即100到139)。 +```c +/* http://lxr.free-electrons.com/source/include/linux/sched/prio.h#L21 */ +#define MAX_USER_RT_PRIO 100 +#define MAX_RT_PRIO MAX_USER_RT_PRIO + +/* http://lxr.free-electrons.com/source/include/linux/sched/prio.h#L24 */ +#define MAX_PRIO (MAX_RT_PRIO + 40) +#define DEFAULT_PRIO (MAX_RT_PRIO + 20) +``` + + +| 优先级范围 | 描述 | +| ------------- |:-------------:| +| 0——99 | 实时进程 | +| 100——139 | 非实时进程 | + +现在,这个划分是起决定作用的,而且一定是数值越小,优先级越高。值越大静态优先级越低。 + + +但是,有时候从网上会看到优先级数值越大,优先级越高?这又是怎么回事?难道有一种说法错了吗? + +实际的原因是这样的,对于一个实时进程,他有两个参数来表明优先级prio 和rt_priority + +prio才是调度所用的最终优先级数值,这个值越小,优先级越高; +而rt_priority被称作实时进程优先级,他要经过转化 + +``` +prio = MAX_RT_PRIO - 1 - p->rt_priority; +``` + +这样意味着rt_priority值越大,优先级越高; + +而内核提供的修改优先级的函数,是修改rt_priority的值,所以越大,优先级越高 + +所以用户在使用实时进程或线程,在修改优先级时,就会有"优先级值越大,优先级越高的说法",也是对的 + + + +###调度策略相关字段 +------- + +```c +/* http://lxr.free-electrons.com/source/include/linux/sched.h?v=4.5#L1426 */ +unsigned int policy; + +/* http://lxr.free-electrons.com/source/include/linux/sched.h?v=4.5#L1409 */ + +const struct sched_class *sched_class; +struct sched_entity se; +struct sched_rt_entity rt; + + +cpumask_t cpus_allowed; +``` +| 字段 | 描述 | +| ------------- |:-------------:| +| policy | 调度策略 | +| sched_class | 调度类 | +| se | 普通进程的调用实体,每个进程都有其中之一的实体 | +| rt | 实时进程的调用实体,每个进程都有其中之一的实体 | +| cpus_allowed | 用于控制进程可以在哪里处理器上运行 | + +###调度策略 +------- + +policy表示进程的调度策略,目前主要有以下五种: + +>参见 +> +>http://lxr.free-electrons.com/source/include/uapi/linux/sched.h?v=4.5#L36 + +```c +/* +* Scheduling policies +*/ +#define SCHED_NORMAL 0 +#define SCHED_FIFO 1 +#define SCHED_RR 2 +#define SCHED_BATCH 3 +/* SCHED_ISO: reserved but not implemented yet */ +#define SCHED_IDLE 5 +#define SCHED_DEADLINE 6 +``` + +| 字段 | 描述 | 所在调度器类 | +| ------------- |:-------------:|:-------------:| +| SCHED_NORMAL | (也叫SCHED_OTHER)用于普通进程,通过CFS调度器实现。SCHED_BATCH用于非交互的处理器消耗型进程。SCHED_IDLE是在系统负载很低时使用 | CFS | +| SCHED_BATCH | SCHED_NORMAL普通进程策略的分化版本。采用分时策略,根据动态优先级(可用nice()API设置),分配 CPU 运算资源。注意:这类进程比上述两类实时进程优先级低,换言之,在有实时进程存在时,实时进程优先调度。但针对吞吐量优化 | CFS | +| SCHED_IDLE | 优先级最低,在系统空闲时才跑这类进程(如利用闲散计算机资源跑地外文明搜索,蛋白质结构分析等任务,是此调度策略的适用者)| CFS | +| SCHED_FIFO | 先入先出调度算法(实时调度策略),相同优先级的任务先到先服务,高优先级的任务可以抢占低优先级的任务 | RT | +| SCHED_RR | 轮流调度算法(实时调度策略),后 者提供 Roound-Robin 语义,采用时间片,相同优先级的任务当用完时间片会被放到队列尾部,以保证公平性,同样,高优先级的任务可以抢占低优先级的任务。不同要求的实时任务可以根据需要用sched_setscheduler()API 设置策略 | RT | +| SCHED_DEADLINE | 新支持的实时进程调度策略,针对突发型计算,且对延迟和完成时间高度敏感的任务适用。基于Earliest Deadline First (EDF) 调度算法| + +###调度类 +------- + +sched_class结构体表示调度类,目前内核中有实现以下四种: + +```c +extern const struct sched_class stop_sched_class; +extern const struct sched_class dl_sched_class; +extern const struct sched_class rt_sched_class; +extern const struct sched_class fair_sched_class; +extern const struct sched_class idle_sched_class; +``` + + +| 调度器类 | 描述 | +| ------------- |:-------------:| +| idle_sched_class | 每个cup的第一个pid=0线程:swapper,是一个静态线程。调度类属于:idel_sched_class,所以在ps里面是看不到的。一般运行在开机过程和cpu异常的时候做dump | +| stop_sched_class | 优先级最高的线程,会中断所有其他线程,且不会被其他任务打断。作用:1.发生在cpu_stop_cpu_callback 进行cpu之间任务migration;2.HOTPLUG_CPU的情况下关闭任务。| +| rt_sched_class | RT,作用:实时线程 | +| fair_sched_class | CFS(公平),作用:一般常规线程 | + +目前系統中,Scheduling Class的优先级顺序为StopTask > RealTime > Fair > IdleTask + +开发者可以根据己的设计需求,來把所属的Task配置到不同的Scheduling Class中. \ No newline at end of file