【原创】(六)Linux进程调度-实时调度器
背景
Read the fucking source code!
--By 鲁迅A picture is worth a thousand words.
--By 高尔基
说明:
- Kernel版本:4.14
- ARM64处理器,Contex-A53,双核
- 使用工具:Source Insight 3.5, Visio
1. 概述
在Linux内核中,实时进程总是比普通进程的优先级要高,实时进程的调度是由Real Time Scheduler(RT调度器)
来管理,而普通进程由CFS调度器
来管理。
实时进程支持的调度策略为:SCHED_FIFO
和SCHED_RR
。
前边的系列文章都是针对CFS调度器
来分析的,包括了CPU负载
、组调度
、Bandwidth控制
等,本文的RT调度器
也会从这些角度来分析,如果看过之前的系列文章,那么这篇文章理解起来就会更容易点了。
前戏不多,直奔主题。
2. 数据结构
有必要把关键的结构体罗列一下了:
struct rq
:运行队列,每个CPU都对应一个;struct rt_rq
:实时运行队列,用于管理实时任务的调度实体;struct sched_rt_entity
:实时调度实体,用于参与调度,功能与struct sched_entity
类似;struct task_group
:组调度结构体;struct rt_bandwidth
:带宽控制结构体;
老规矩,先上张图,捋捋这些结构之间的关系吧:
- 从图中的结构组织关系看,与
CFS调度器
基本一致,区别在与CFS调度器
是通过红黑树来组织调度实体,而RT调度器
使用的是优先级队列来组织实时调度实体; rt_rq
运行队列,维护了100个优先级的队列(链表),优先级0-99,从高到底;- 调度器管理的对象是调度实体,任务
task_struct
和任务组task_group
都是通过内嵌调度实体的数据结构,来最终参与调度管理的; task_group
任务组调度,自身为每个CPU维护rt_rq
,用于存放自己的子任务或者子任务组,子任务组又能往下级联,因此可以构造成树;
上述结构体中,struct rq
和struct task_group
,在前文中都分析过。
下边针对RT运行队列相关的关键结构体,简单注释下吧:
struct sched_rt_entity {
struct list_head run_list; //用于加入到优先级队列中
unsigned long timeout; //设置的时间超时
unsigned long watchdog_stamp; //用于记录jiffies值
unsigned int time_slice; //时间片,100ms,
unsigned short on_rq;
unsigned short on_list;
struct sched_rt_entity *back; //临时用于从上往下连接RT调度实体时使用
#ifdef CONFIG_RT_GROUP_SCHED
struct sched_rt_entity *parent; //指向父RT调度实体
/* rq on which this entity is (to be) queued: */
struct rt_rq *rt_rq; //RT调度实体所属的实时运行队列,被调度
/* rq "owned" by this entity/group: */
struct rt_rq *my_q; //RT调度实体所拥有的实时运行队列,用于管理子任务或子组任务
#endif
} __randomize_layout;
/* Real-Time classes' related field in a runqueue: */
struct rt_rq {
struct rt_prio_array active; //优先级队列,100个优先级的链表,并定义了位图,用于快速查询
unsigned int rt_nr_running; //在RT运行队列中所有活动的任务数
unsigned int rr_nr_running;
#if defined CONFIG_SMP || defined CONFIG_RT_GROUP_SCHED
struct {
int curr; /* highest queued rt task prio */ //当前RT任务的最高优先级
#ifdef CONFIG_SMP
int next; /* next highest */ //下一个要运行的RT任务的优先级,如果两个任务都有最高优先级,则curr == next
#endif
} highest_prio;
#endif
#ifdef CONFIG_SMP
unsigned long rt_nr_migratory; //任务没有绑定在某个CPU上时,这个值会增减,用于任务迁移
unsigned long rt_nr_total; //用于overload检查
int overloaded; //RT运行队列过载,则将任务推送到其他CPU
struct plist_head pushable_tasks; //优先级列表,用于推送过载任务
#endif /* CONFIG_SMP */
int rt_queued; //表示RT运行队列已经加入rq队列
int rt_throttled; //用于限流操作
u64 rt_time; //累加的运行时,超出了本地rt_runtime时,则进行限制
u64 rt_runtime; //分配给本地池的运行时
/* Nests inside the rq lock: */
raw_spinlock_t rt_runtime_lock;
#ifdef CONFIG_RT_GROUP_SCHED
unsigned long rt_nr_boosted; //用于优先级翻转问题解决
struct rq *rq; //指向运行队列
struct task_group *tg; //指向任务组
#endif
};
struct rt_bandwidth {
/* nests inside the rq lock: */
raw_spinlock_t rt_runtime_lock;
ktime_t rt_period; //时间周期
u64 rt_runtime; //一个时间周期内的运行时间,超过则限流,默认值为0.95ms
struct hrtimer rt_period_timer; //时间周期定时器
unsigned int rt_period_active;
};
3. 流程分析
3.1 运行时统计数据
运行时的统计数据更新,是在update_curr_rt
函数中完成的:
update_curr_rt
函数功能,主要包括两部分:- 运行时间的统计更新处理;
- 如果运行时间超出了分配时间,进行时间均衡处理,并且判断是否需要进行限流,进行了限流则需要将RT队列出队,并重新进行调度;
为了更直观的理解,下边还是来两张图片说明一下:
sched_rt_avg_update
更新示意如下:
rq->age_stamp
:在CPU启动后运行队列首次运行时设置起始时间,后续周期性进行更新;rt_avg
:累计的RT平均运行时间,每0.5秒减半处理,用于计算CFS负载减去RT在CFS负载平衡中使用的时间百分比;
3.2 组调度
RT调度器
与CFS调度器
的组调度基本类似,CFS调度器
的组调度请参考(四)Linux进程调度-组调度及带宽控制
。
看一下RT调度器
组调度的组织关系图吧:
- 系统为每个CPU都分配了RT运行队列,以及RT调度实体,任务组通过它包含的RT调度实体来参与调度;
- 任务组
task_group
的RT队列,用于存放归属于该组的任务或子任务组,从而形成级联的结构;
看一下实际的组织示意图:
3.3 带宽控制
请先参考(四)Linux进程调度-组调度及带宽控制
。
RT调度器
在带宽控制中,调度时间周期设置的为1s,运行时间设置为0.95s:
/*
* period over which we measure -rt task CPU usage in us.
* default: 1s
*/
unsigned int sysctl_sched_rt_period = 1000000;
/*
* part of the period that we allow rt tasks to run in us.
* default: 0.95s
*/
int sysctl_sched_rt_runtime = 950000;
这两个值可以在用户态通过/sys/fs/cgroup/cpu/rt_runtime_us
和/sys/fs/cgroup/cpu/rt_period_us
来进行设置。
看看函数调用流程:
init_rt_bandwidth
函数在创建分配RT任务组的时候调用,完成的工作是将rt_bandwidth
结构体的相关字段进行初始化:设置好时间周期rt_period
和运行时间限制rt_runtime
,都设置成默认值;- 可以从用户态通过操作
/sys/fs/cgroup/cpu
下对应的节点进行设置rt_period
和rt_runtime
,最终调用的函数是tg_set_rt_bandwidth
,在该函数中会从下往上的遍历任务组进行设置时间周期和限制的运行时间; - 在
enqueue_rt_entity
将RT调度实体入列时,最终触发start_rt_bandwidth
函数执行,当高精度定时器到期时调用do_sched_rt_period_timer
函数; do_sched_rt_period_timer
函数,会去判断该RT运行队列的累计运行时间rt_time
与设置的限制运行时间rt_runtime
之间的大小关系,以确定是否限流的操作。在这个函数中,如果已经进行了限流操作,会调用balance_time
来在多个CPU之间进行时间均衡处理,简单点说,就是从其他CPU的rt_rq队列中匀出一部分时间增加到当前CPU的rt_rq队列中,也就是将当前rt_rt运行队列的限制运行时间rt_runtime
增加一部分,其他CPU的rt_rq运行队列限制运行时间减少一部分。
来一张效果示意图:
3.4 调度器函数分析
来一张前文的图:
看一下RT调度器实例的代码:
const struct sched_class rt_sched_class = {
.next = &fair_sched_class,
.enqueue_task = enqueue_task_rt,
.dequeue_task = dequeue_task_rt,
.yield_task = yield_task_rt,
.check_preempt_curr = check_preempt_curr_rt,
.pick_next_task = pick_next_task_rt,
.put_prev_task = put_prev_task_rt,
#ifdef CONFIG_SMP
.select_task_rq = select_task_rq_rt,
.set_cpus_allowed = set_cpus_allowed_common,
.rq_online = rq_online_rt,
.rq_offline = rq_offline_rt,
.task_woken = task_woken_rt,
.switched_from = switched_from_rt,
#endif
.set_curr_task = set_curr_task_rt,
.task_tick = task_tick_rt,
.get_rr_interval = get_rr_interval_rt,
.prio_changed = prio_changed_rt,
.switched_to = switched_to_rt,
.update_curr = update_curr_rt,
};
3.4.1 pick_next_task_rt
pick_next_task_rt
函数是调度器用于选择下一个执行任务。流程如下:
- 与
CFS调度器
不同,RT调度器
会在多个CPU组成的domain
中,对任务进行pull/push
处理,也就是说,如果当前CPU的运行队列中任务优先级都不高,那么会考虑去其他CPU运行队列中找一个更高优先级的任务来执行,以确保按照优先级处理,此外当前CPU也会把任务推送到其他更低优先级的CPU运行队列上。 _pick_next_task_rt
的处理逻辑比较简单,如果实时调度实体是task
,则直接查找优先级队列的位图中,找到优先级最高的任务,而如果实时调度实体是task_group
,则还需要继续往下进行遍历查找;
关于任务的pull/push
,linux提供了struct plist
,基于优先级的双链表,其中任务的组织关系如下图:
pull_rt_task
的大概示意图如下:
- 当前CPU上的优先级任务不高,从另一个CPU的
pushable_tasks
链表中找优先级更高的任务来执行;
3.4.2 enqueue_task_rt/dequeue_task_rt
当RT任务进行出队入队时,通过enqueue_task_rt/dequeue_task_rt
两个接口来完成,调用流程如下:
enqueue_task_rt
和dequeue_task_rt
都会调用dequeue_rt_stack
接口,当请求的rt_se对应的是任务组时,会从顶部到请求的rt_se将调度实体出列;- 任务添加到rt运行队列时,如果存在多个任务可以分配给多个CPU,设置overload,用于任务的迁移;
有点累了,收工了。
【原创】(六)Linux进程调度-实时调度器的更多相关文章
- 【原创】(五)Linux进程调度-CFS调度器
背景 Read the fucking source code! --By 鲁迅 A picture is worth a thousand words. --By 高尔基 说明: Kernel版本: ...
- 转: 调整 Linux I/O 调度器优化系统性能
转自:https://www.ibm.com/developerworks/cn/linux/l-lo-io-scheduler-optimize-performance/index.html 调整 ...
- 【原创】(四)Linux进程调度-组调度及带宽控制
背景 Read the fucking source code! --By 鲁迅 A picture is worth a thousand words. --By 高尔基 说明: Kernel版本: ...
- Linux I/O 调度器
每个块设备或者块设备的分区,都对应有自身的请求队列, 而每个请求队列都可以选择一个I/O调度器来协调所递交的.I/O调度器的基本目的是将请求按照它们对应在块设备上的扇区号进行排列,以减少磁头的移动, ...
- Linux进程调度器的设计--Linux进程的管理与调度(十七)
1 前景回顾 1.1 进程调度 内存中保存了对每个进程的唯一描述, 并通过若干结构与其他进程连接起来. 调度器面对的情形就是这样, 其任务是在程序之间共享CPU时间, 创造并行执行的错觉, 该任务分为 ...
- 【原创】(一)Linux进程调度器-基础
背景 Read the fucking source code! --By 鲁迅 A picture is worth a thousand words. --By 高尔基 说明: Kernel版本: ...
- linux调度器源码分析 - 概述(一)
本文为原创,转载请注明:http://www.cnblogs.com/tolimit/ 引言 调度器作为操作系统的核心部件,具有非常重要的意义,其随着linux内核的更新也不断进行着更新.本系列文章通 ...
- Linux进程管理 (2)CFS调度器
关键词: 目录: Linux进程管理 (1)进程的诞生 Linux进程管理 (2)CFS调度器 Linux进程管理 (3)SMP负载均衡 Linux进程管理 (4)HMP调度器 Linux进程管理 ( ...
- Linux进程管理 (9)实时调度类分析,以及FIFO和RR对比实验
关键词:rt_sched_class.SCHED_FIFO.SCHED_RR.sched_setscheduler().sched_setaffinity().RR_TIMESLICE. 本文主要关注 ...
随机推荐
- OAuth 2.0学习笔记
文章目录 OAuth的作用就是让"客户端"安全可控地获取"用户"的授权,与"服务商提供商"进行互动. OAuth在"客户端&quo ...
- Docker容器时间同步问题
具体操作: 为了保证容器和宿主机之间的时间同步,采用如下参数:-v /etc/localtime:/etc/localtime:ro但是在页面访问的时候时间依然相差8个小时: 该怎么破解! 回复: 1 ...
- 没有admin权限如何免安装使用Node和NPM
此教程只针对于在windows系统上没有admin权限和软件安装权限,但是又希望能像安装版一样使用Node和NPM的用户. 步骤一: 下载压缩版node 访问https://nodejs.org/en ...
- 从谷歌到脸书:为何巨头纷纷“钟情于”VR相机?
VR的火爆,自然无需多言.而基于VR这一个概念,已经在多个相关行业不断衍生出新的产品.服务或内容.VR眼镜.VR头盔.VR相机.VR游戏.VR影视.VR应用--但VR产业的发展并不是齐头并进,而是出现 ...
- 推荐系统--隐语义模型LFM
主要介绍 隐语义模型 LFM(latent factor model). 隐语义模型最早在文本挖掘领域被提出,用于找到文本的隐含语义,相关名词有 LSI.pLSA.LDA 等.在推荐领域,隐语义模型也 ...
- 如何提高码农产量,基于java的web快速开发平台之自定义表单开发随笔
老板 :下班前一定写完? 程序猿:可以,下班前能一定给! 第二天早上上班~~~ 老板:这都第二天了,怎么没写完? 程序猿:我还没有下班呢! 哎!程序猿的痛啊 公司上线的项目有不少销售记录表,又是报价单 ...
- EOS2.0环境搭建-centos7
需要安装启动的有三个组件 nodes,keosd,cleos,看看三者的关系 nodeos:核心程序,用于启动eos节点服务,在后台运行,可以配置不同 插件.该进程负责账户管理.区块生成.共识建立,并 ...
- 7-2 jmu-python-九九乘法表(矩形) (10 分)
本题目要求输出如下图所示的九九乘法表 注:乘积要求做格式控制,占4个位置的宽度 输入样例: 无 输出样例: 1*1=1 1*2=2 1*3=3 1*4=4 1*5=5 1*6=6 1*7=7 1*8= ...
- @JsonFormat、@DateTimeFormat注解,读取数据库晚一天问题
@DateTimeFormat(pattern = "yyyy-MM-dd") @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss&qu ...
- node--非阻塞式I/O,单线程,异步,事件驱动
1.单线程 不同于其他的后盾语言,node是单线程的,大大节约服务器开支 node不为每个客户创建一个新的线程,仅使用一个线程.通过非阻塞I/O以及 事件驱动机制,使其宏观上看是并发的,可以处理高并发 ...